Extension
Methods are just one of the many impressive features introduced with C# 3.0
(along with the Implicitly Typed Local Variables, Lambda Expressions, LINQ and
other wonders of the .NET)
Sure, you
will say, even kindergarten developers know that! But what exactly are those
Extension Methods?
What kind of magic makes them possible and how / when (not) to use them?
In this article we will first briefly examine this new language feature, show some basics usages and then we will tackle some more advanced and concrete examples of Extension Methods used to provide natural DateTime operations (similar to Date and Time operations available in Ruby on Rails) so be sure to stay tuned!
First of all lets start looking at the roots of it and see what the almighty MSDN Documentation says on this subject:
What kind of magic makes them possible and how / when (not) to use them?
In this article we will first briefly examine this new language feature, show some basics usages and then we will tackle some more advanced and concrete examples of Extension Methods used to provide natural DateTime operations (similar to Date and Time operations available in Ruby on Rails) so be sure to stay tuned!
First of all lets start looking at the roots of it and see what the almighty MSDN Documentation says on this subject:
“Extension
methods enable you to "add" methods to existing types without
creating a new derived type, recompiling, or otherwise modifying the original
type.
For
client code written in C# and Visual Basic, there is no apparent difference
between calling an extension method and the methods that are actually defined
in a type. “
Well that
sure sounds nice!
Until now we were kind of stuck with the existing built-in classes and types, and in order to extend them we had to inherit the new class from them and then add new functionality (if we were lucky enough that the original class is not marked as Sealed).
Until now we were kind of stuck with the existing built-in classes and types, and in order to extend them we had to inherit the new class from them and then add new functionality (if we were lucky enough that the original class is not marked as Sealed).
So with
Extension Methods we get some new degrees of freedom.
We can
add functionality to any existing class (for now only in the form of new static
methods, but who knows, we might get some more options in the future, like
Extension Properties and Extension Events etc).
Off
course there are some limitations:
- Extension Methods have to be implemented as static methods and in static classes (inside a non-nested, non-generic static class to be more precise).
- You can use extension methods to extend a class or interface, but not to override them. An extension method with the same name and signature as an interface or class method will never be called. At compile time, extension methods always have lower priority than instance methods defined in the type itself.
- Extension methods cannot access private variables in the type they are extending.
You can
consider Extension Methods as a 'legal' way to add more static methods to
existing classes without actually inheriting them.
But the
funny thing is that unlike regular static methods of the class, you cannot call
Extension Methods on a class level (you will get an compile time error if you
try this), but instead you must invoke them on a instance of the class (as if
they were some regular methods of the instance of that class, which they are
not!!!).
Also, inside the Extension Method you can freely use public properties of the passed object instance on which the method is being invoked, you are by no means limited only to static object data. Only the Extension Method is static method, but the object on which is called is full, regular object instance.
Also, inside the Extension Method you can freely use public properties of the passed object instance on which the method is being invoked, you are by no means limited only to static object data. Only the Extension Method is static method, but the object on which is called is full, regular object instance.
Its
little confusing but don't worry you will get used to it as soon as you start
using it.
How this works under the hood? (again MSDN to the rescue):
"In your code you invoke the extension method with instance method syntax.
However, the intermediate language (IL) generated by the compiler translates your code into a call on the static method. Therefore, the principle of encapsulation is not really being violated."
How this works under the hood? (again MSDN to the rescue):
"In your code you invoke the extension method with instance method syntax.
However, the intermediate language (IL) generated by the compiler translates your code into a call on the static method. Therefore, the principle of encapsulation is not really being violated."
Ok, lets
start with some simple "Hello World" example (for those of you that
want more advanced examples, be patient we will get to that soon, or just skip
to the end of article):
Motivation: We will
add a single Extension Method called Reverse() to the System.String
class that will return the reversed copy of the string on which we will call
this method.
First we
must create a static class with the Reverse() static method.
Also we will define it inside a namespace so we can later import that Extension Method in our projects:
Also we will define it inside a namespace so we can later import that Extension Method in our projects:
using System;
namespace StringExtensionMethods
{
public static class StringHelpers
{
public static string Reverse (this string s)
{
char[] letters = s.ToCharArray();
Array.Reverse(letters);
return new string(letters);
}
}
}
If you
look at Reverse() method signature you can notice the 'this'
keyword that is mandatory when creating Extension Methods (this keyword
actually tells the compiler that we are creating one).
Also you
can see that 'this' keyword is followed by the
actual type that we are extending (in this case its string
type) and after this there is a name of
the parameter (so we can reference it in the methods implementation) and we can
choose the name as we wish, no especial limitations there.
Make note
that the 'string s' parameter that the static
method will receive will be the instance of the object on which we are calling
the actual Extension Method.
IMPORTANT:
Remember
that when we will call the Reverse() method there is no need to pass that first
parameter, runtime will do that 'automagically' for us. But more on that later.
So first
lets see how to use this Reverse() method:
In our
project we must include the namespace where we defined the Extension Method so
that it become available.
using StringExtensionMethods;
And then
in our code we can write:
string word = "alpha";
Console.WriteLine(word.Reverse());
and the
text that will be printed in the console will be “ahpla”.
As we
have already said, you see there is no need to pass the first parameters to our
Extension Method because the instance of the object on which we are calling it
is passed as the first parameter.
Off
course, that does not mean we cannot pass any parameters!
We can
add more parameters to the the method signature and then pass them when calling
the extension method.
But the
first parameter will always be the instance of the object we are using the
method on.
We can
optionally define (in Extension Method signature) and later (when calling)
provide arguments for parameters 2 through n.
So lets quickly create an example with additional parameter. This time we will extend System.Int type and add to it MultiplyBy() method that will allow us to easily multiply any integer with another integer.
Here is the implementation:
So lets quickly create an example with additional parameter. This time we will extend System.Int type and add to it MultiplyBy() method that will allow us to easily multiply any integer with another integer.
Here is the implementation:
namespace IntExtensionMethods
{
public static class IntHelpers
{
public static int MultiplyBy(this int originalNumber, int numberToMultiplyWith)
{
return originalNumber * numberToMultiplyWith;
}
}
}
You see
that we have added the second Int parameter to the method signature and we use
it to multiply the original number with this second parameter called
numberToMultiplyWith.
And here is how to use it:
First we must not forget to add the namespace to our using list:
And here is how to use it:
First we must not forget to add the namespace to our using list:
using IntExtensionMethods;
and then call our method on the instance of any Int with single parameter (as we said many times so far, the first parameter - the actual Int value on which we are calling the method - is passed by runtime so we dont have to do it):
int resultShouldBeTwelve = 3.MultiplyBy(4);
Console.WriteLine(result.ToString());
Thats about it. Now we know how to use Extension Methods!
But wait,
we are not finished yet! This is where all the fun begins.
Lets do
some real magic. Imagine we have a project that will be heavily using DateTime
object and making a lot of complex date and time calculations.
Even with
all the nice functionality built-in into DateTime class still its sometimes
tedious to do time calculations.
If you
want to add two weeks to some DateTime instance here is how we would normally
had to do it:
DateTime nextWeek = DateTime.Now + new TimeSpan(14, 0, 0, 0);
or the other way:
or the other way:
nextWeek = DateTime.Now + TimeSpan.FromDays(14);
Looks a little messy, right? Wouldn't it be more natural for us as humans to write our code something like this:
DateTime nextWeek = DateTime.Now + 2.Weeks();
or even nicer:
or even nicer:
DateTime twoWeeksFromNow = 2.Weeks().FromNow();
Well, hold your breath, because with extension methods its very easy to accomplish this.
For
example, here is the Extension Method for the System.Int32 class that returns
TimeSpan of the length of the number of weeks depending on the value of
the number (Int) on which you call it:
/// <summary>
/// Returns TimeSpan for given number of
Weeks (number of weeks * 7)
/// </summary>
/// <param name="weeks"></param>
/// <returns></returns>
public static TimeSpan Weeks(this int weeks)
{
return new TimeSpan(weeks * 7, 0, 0, 0, 0);
}
But this is just scratching the surface.
Here is
some more functionality we could easily achieve combining that with more
similar Extension Methods:
DateTime tomorrow = DateTime.Now + 1.Days();
DateTime yesterday = DateTime.Now - 1.Days();
DateTime changedHourTo14h = DateTime.Now.SetHour(14);
DateTime todayNoon = DateTime.Now.Noon();
DateTime tomorrowNoon = DateTime.Now.NextDay().Noon();
DateTime fiveDaysAgo = 5.Days().Ago();
DateTime twoDaysFromNow = 2.Days().FromNow();
DateTime nextYearSameDateAsTodayNoon = 1.Years().FromNow().Noon();
Now how
about that ???
If you got interested make sure you check out my CodePlex project Fluent DateTime that provides these and many more nice Extension Methods for handling DateTime.
If you got interested make sure you check out my CodePlex project Fluent DateTime that provides these and many more nice Extension Methods for handling DateTime.
If you have some ideas you can join the project and contribute to the .NET community.
This wraps up our journey to the Extension Methods.
Now
before you get all excited and start writing them for each class in System namespace
just one final note of warning:
Make sure
that you don't use this powerful feature without self-control.
If used
when not really necessary you are opening the Pandora's Box and you can make
your code less readable, or you can find yourself writing Extension Methods to
some class and instead what you should have done is to extend that class and
add those methods to the descendant classes lower down the class hierarchy...
Here is
what MSDN says on this subject:
“Extension
methods are less discoverable and more limited in functionality than instance
methods. For those reasons, it is recommended that extension methods be used
sparingly and only in situations where instance methods are not feasible or
possible.”
So use
it, but wisely!
No comments :
Post a Comment