Ahmed El-Halwagy’s Blog

C# 3.0 New Features – What Made LINQ Possible (Part 2). Extension Methods

Posted on: November 20, 2008

string name = “Ahmed Halwagy”;

if (name.IsHalwagy())

Console.WriteLine(“Yeap”);

else

Console.WriteLine(“Not halwagy”);

Shocked? .. Cool!
Now I assume that two questions just pumped into your mind:
1- What the heck is that IsHalwagy() method?
2- Would Microsoft ever -for any reason- reshape their string object to add this method?

I will answer your questions from bottom up. Question number 2, the answer is No. Though Microsoft really pursue my own satisfaction, but they never went this far -but I really encourage you to email the C# team and ask them for IsHalwagy() method on the string class(just kidding 🙂 ).

Question number 1, the answer is: It’s my own method. I’ve extended the string class by this IsHalwagy() method, and believe it or not, I didn’t have to use classes from the System.Reflection.Emit namespace, actually the job was much easier(as easy as typing 3 lines of code J ).

Confused! That’s good. Now relax and take a deep breath.
As you know, C# types (classes, interfaces, delegates, structs, enums)once compiled into an assembly, they’re final, meaning that the only way to change them is to have their source code, edit it, and then recompile. However, this is not the case in C# 3.0; in C# 3.0 you can extend any compiled-final- .NET type using a very easy and convenient way through Extension Methods.

Though the usefulness of this capability is extremely obvious in the previous IsHalwagy() example, let’s consider a more useful scenario –at least more useful for you.
<!–[if !supportLineBreakNewLine]–>
<!–[endif]–>

static class IntOperations {

public static bool IsEven(int x) {

return (x % 2) == 0;

}

public static long Factorial(int x) {

if (x <= 1)

return 1;

else

return x * Factorial(x – 1);

}

}

 

In the previous example we have a class called IntOperations this class contains some of the operations than you would need to perform on any integer. The typical use for the methods in this class would be ass follows:

int x = 5;

Console.WriteLine(“The factorial of {0} = {1}”, x,IntOperations.Factorial(x));

Console.WriteLine(IntOperations.IsEven(5));

By using extension methods you can alter the previous example to use the Factorial(int) and the IsEven(int) methods as if they were originally built into the System.Int32 struct. The only change that is required in order to achieve that is to add 8 characters to your methods declaration, as follows:

static class IntOperations {

//Notice the this modifier precedding the parameter declaration.

public static bool IsEven(this int x) {

return (x % 2) == 0;

}

public static long Factorial(this int x) {

if (x <= 1)

return 1;

else

return x * Factorial(x – 1);

}

}
Here all I’ve done is simply adding the “this” modifier before the first parameter of each method declaration. Adding the “this” modifier to the declaration of the int parameter simply instructs the compiler to deal with method as if it was originally built into the System.Int32 type.Voila! that’s it. By doing so you can simply call your methods on any instance of type System.Int32 as if it was originally coded into it:

int x = 5;

Console.WriteLine(“The factorial of {0} = {1}”, x, x.Factorial());

Console.WriteLine(x.IsEven());

Now let’s take alook at what’s really happening behind the scens. If you compile this project and open the assembly using a tool like (ILDASM) you will find that the compiler is invoking the static methods in a very normal manner (i.e. IntOperations.Factorial(x)). What you really saw of x.Factorial() is just some smoke-and-mirror effect. Here’s a part of the IL viewed by IlDASM for this assembly:

 

.method private hidebysig static void Main(string[] args) cil managed

{

.entrypoint

// Code size 44 (0x2c)

.maxstack 3

.locals init ([0] int32 x)

IL_0000: nop

IL_0001: ldc.i4.5

IL_0002: stloc.0

IL_0003: ldstr “The factorial of {0} = {1}”

IL_0008: ldloc.0

IL_0009: box [mscorlib]System.Int32

IL_000e: ldloc.0

IL_000f: call int64 ConsoleApplication3.IntOperations::Factorial(int32)

IL_0014: box [mscorlib]System.Int64

IL_0019: call void [mscorlib]System.Console::WriteLine(string,

object,

object)

IL_001e: nop

IL_001f: ldloc.0

IL_0020: call bool ConsoleApplication3.IntOperations::IsEven(int32)

IL_0025: call void [mscorlib]System.Console::WriteLine(bool)

IL_002a: nop

IL_002b: ret

} // end of method Program::Main

 

 

Ps: the compiler calls the extension methods statically using their clas names.

 

Extension Methods Restrictions:

<!–[if !supportLists]–>1- <!–[endif]–> EExtension methods must be declared in static class, and there for they must be static as well (remember static classes can only contain static methods, and static fields).

<!–[if !supportLists]–>2- <!–[endif]–> Normal object instance methods take precedence over extension methods if they happened to be with the same signuature.

 

Importing namespaces containing types that define Extension Methods:

Namespaces containing the classes that contain extension methods have to imported in order to be able to use the extension methods defined on them.
Let’s take a while discussing this poing, now consider that my IntOperations class is declared inside a namespace called MyEXTNamespace, and the program class containing the main method is declared inside a namespcae called MyAssemblyNamespace. The code will look like the following.
using System;

using System.Linq;

namespace MyAssembly {

class Program {

static void Main(string[] args) {

int x = 5;

Console.WriteLine(“The factorial of {0} = {1}”, x, x.Factorial());

Console.WriteLine(x.IsEven());

}

}

}

namespace MyEXTNamespace {

static class IntOperations {

//Notice the this modifier precedding the parameter declaration.

public static bool IsEven(this int x) {

return (x % 2) == 0;

}

public static long Factorial(this int x) {

if (x <= 1)

return 1;

else

return x * Factorial(x – 1);

}

}

}

If you try to compile this code you will get compile time errors indicating that System.Int32 does not containg a definition fo IsEven() and Factorial(). Can you see it? Can you see the problem?

The problem is you declared your extension methods in MyEXTNamespace and in order to use these methods outside the scope of this namespace you have to import this namespace by adding a using directive to MyEXTNamespace namespace at the top of your file as follows:

using MyEXTNamespace;

Problem Solved!

 

Using Extension Libraries:

Now as you can see it can be very useful if you are able to use your extension methods in many projects as any other .NET library. This is absolutely possible. You can simply move your MyEXTNamespace namespace to another class library project and build this library and simply reference the output assembly from any other .NET project and reuse. Just remember to mark the classes containing the extension methods with the public access modifier in order to be used cross assemblies.

 

A Final Detail:

One last thing you should know about extension methods is that they apply to inheritance concepty i.e. if you extended a parent class with some method, all the derived classes will inherit this method. Take a look at the following example:

public static string ReturnYourParentType(this object o) {

Type t = o.GetType();

if (t.BaseType == null)

return “Don’t have a parent, I’m object”;

return t.BaseType.FullName;

}

 

Here I’ve extended the System.Object class with a method that returns the name of the parent type of any object. Now watch me invoking this method:

 

object o = new object();

Console.WriteLine(o.ReturnYourParentType());

 

int x = 5;

//it applies to int too. because int is an object.

string intParent = x.ReturnYourParentType();

Console.WriteLine(“intParent = {0}”, intParent);

// and applies to string too. again because string is also an object.

string stringParent = “hello”.ReturnYourParentType();

Console.WriteLine(“stringParent = {0}”, stringParent);

 

As you can see here I’ve invoked this method on any thing of type object, and of course in .NET every thing is an object. So because every thing inherits from object, I can call my ReturnYourParentType() on any thing.

 

Advertisements

3 Responses to "C# 3.0 New Features – What Made LINQ Possible (Part 2). Extension Methods"

Yeeeeeah ,finnaly i’ve finished
congratulations Mr.Ahmed on your blog and specially for the last topic you post.

How Are You Mr??? what about your news and your new work??all IBM Miss you.

Do You Know me first???????????i’m walaa from IBM Tanta. 🙂

Got an interesting question about this post from Hema Youns .. He asked what if two classes have the same extension method that targets System.Int32 for example, which method will be executed?

well, the answer simply is “it’s a matter of scope”, meaning that the closer method will be executed, so if MyExtensionMethod is declared in a class that is in the same namespace where I use it, and another MyExtensionMethod in declared in a class in another namespace (even if i’m importing this namespace), the method in the same namespace will be executed. here’s a code example.

namespace DefaultProgram {
class Program {
static void Main(string[] args) {
int x = 10;
x.MyExtensionMethod();
}
}
static class MyExts{
public static void MyExtensionMethod(this int x) {
Console.WriteLine(“Called from inside MyExts class”);
}
}
}
namespace AnotherNameSpace {
public static class SomeOtherExts {
public static void MyExtensionMethod(this int x) {
Console.WriteLine(“Called On SomeOtherExts class in AnotherNameSpace”);
}
}
}

this will run the method in the same namespace (DefaultProgram).
However if the methods exist in two classes that are defined withing the same namspace, an error is going to be generated by the compiler as it couldn’t resolve this ambiguous call. And this will also be the case if the two classes in two different namespaces that neither of them is the default namespace where we invoke the mthods. From this you can see that it’s purely a matter of scope.

Another interesting fact about Extension Methods is that you actually can call them on null reference . You might know that you can’t call any method on a null reference, otherwise a very famous, rather common, NullReferenceException will be thrown. for example :
object y = null;
y.ToString(); //Definitly invalid
but with extension methods, it’s different -of course this is according to the fact that extension methods are not instance level methods; instead they are normal static methods which do not need an instance to be called. take a look at the following example :
public static class MyExts {
public static bool IsNull(this object y) {
return y == null;
}
}
if you would to use this method in the two coming scenarios :
Scenario 1-
object x = null;
Console.WriteLine(x.IsNull());

Scenario 2-
object x = new object();
Console.WriteLine(x.IsNull());
the output for these two scenarios will be “True” and “False” respectively. Again calling this method on a null object here is valid because the method is actually not part of that object. it’s a normal static (class-level) method.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Calendar

November 2008
S M T W T F S
    Dec »
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Categories

Delicious

Recent Twitter Updates

Error: Twitter did not respond. Please wait a few minutes and refresh this page.

%d bloggers like this: