I personally think that extension methods are a double edged sword. They can both add readability to code and make it obscure at the same time. As the name suggest, they extend functionality of a class. You may now be thinking that wait didn't we use inheritance for that purpose?
The answer is : well, maybe...it all depends. Inheritance is used when there is a form of specialization going on and an added state is required for all the objects of the extended class. There are times where you don't really need to add this functionality to the class since you're not specializing anything. Nonetheless, this would rarely keep you from extending that type if you own the code. But there are times when that is basically not an option.
For example, when you don't own the class's code or you are adding functionality to an interface, you can't really make any changes to legacy code. At these times you may add a static method and just pass in the class to the method for that functionality. Although this technique is widely used, this wouldn't feel that much object oriented when you look at the code since the functionality is not really called on the object itself.
In case of interfaces, when you add a method to an interface you have basically broken all legacy code that used that interface. All those classes wouldn't build anymore and would require implementation of that method. This is due to the rule that states that each implementation of an interface has to implement all the methods that are declared in that interface.
Finally Changes that LINQ needed are mostly to the interfaces. This is why extension methods were added to the language. Yes the language only. As you have probably guessed by now the changes are yet again only syntactic sugar added by the compiler. Extension methods are converted to a static class that receives each class as an argument behind the scenes. The syntax just make it look like the type now has the functionality.
Declaring extension methods is almost too simple to forget! All you have to do is adding the extended type to as a first parameter to the method and add a "this" keyword before the type too:
public static int MultiplyBy(this int extendedType, int multiplyBy)
{
return extendedType * multiplyBy;
}
Now every integer has a "MultiplyBy" method. It is truly as easy as that. There are some limitation as to where the extension method can be declared. The class in which the extension method is implemented should be non-nested, non-generic and static.
Extension methods are resolved as follows:
The compiler would firstly look for an instance method with the signature defined in the caller code. If there no instance methods found in the type then all imported namespaces are searched for compatible extension methods. Compatible extension methods are those that have exactly the same syntax or have signature that can be implicitly converted. Note that if an extension method has the same signature as an instance method, the extension methods is never called. Also no warning is given by the compiler on the occurrence of said event.
Now we look at another piece of code:
public static bool Equals(this object obj1, object obj2)
{
if(obj1 == null)
return (obj1 == obj2);
else
return obj1.Equals(obj2);
}
Object obj1 = null;
Object obj2 = new Object();
Console.WriteLine(obj1.Equals(obj2));
What do you think the outcome of the code above is? Does it compile? Do we get a run-time error? Can we call methods on an object that is null(obj1)? We sure were not able to up to now. The code above compiles and runs without issue. The reason is that you can actually call extension methods on NULL objects ! If you think about it, it kind of makes sense. After all we talked about extension methods being syntactic sugar on top of the language. So actually what is happening is the compiler creating a static class containing the method and passing the object as an argument. There is no problem with an argument being NULL now is it ?
That's all there is to say on extension methods. Next we will look at the extension methods added to .Net 3.5.
IEnumerable<T> Extension Methods
This interface is one those extended with extension methods in .Net 3.5. A host of extension methods that work on the sequence of Ts yielded by the type. Filtering, aggregation, projection, search, grouping are just a few of the uses of these added methods. It is really fun to play around with the methods of this interface. I can't really look at them all in this post and I haven't even used all of them but I'll go through the filtering operation of Where<T>, projection in Select<T> and maybe some groupings.
Where<T> extension method
If you're familiar with SQL you can think of this method as a counterpart to the where clause in a select statement. If you aren't familiar with SQL then an example should make it clear:
Enumerable.Range(1, 10).Where(x => x < 5);
In the example above first the static method Range of the Enumerable static class is used to get the numbers between 1-10. Notice that the return value of this function is an IEnumerable
public static IEnumerable<T> Where<T>(this IEnumerable<T> sequence, Func<T, bool> predicate)
{
if (sequence == null || predicate == null)
throw new ArgumentNullException();
foreach (T element in sequence)
{
if(predicate(element))
yield return element;
}
}
There is nothing to stop us from using a method group or a delegate instead of the lambda expression but usually as you are chaining these methods together you don't usually end up doing something that a lambda expression cannot handle.
Select<T> extension method
This method is also called the projection method. Basically what it does is receiving an input, project that object into another object by either selecting a part of it or adding to it, etc. For example you may have an object that carries around a lot of information and you may want to populate a grid view with the said object. One easy way of populating grid views is to assign a collection to the DataSource property. Since you can't really assign a collection of the entire object you can just select a piece of the object and add them to a collection and then set the property. In the inverse case you can create an anonymous type that contains an object:
var x = Enumerable.Range(1, 10).Select(x => new {Number = x, Inverse = 1/x});
GroupBy<T> extension method
The GroupBy extension method has many overloads and is a very powerful tool. This extension methods would group the elements of the sequence according to a key which is designated in the first argument. The return value of this extension method is IEnumerable<IGrouping<TKey, TElement>>. The IGrouping<TKey, TElement> type is actually inherited from IEnumerable<T> and it also contains a property "Key". It is now kind of clear what this type is there for. The GroupBy method would return a enumeratable list of IGroupings which each contain a key and can also be enumerated. The simplest form can be seen in the example below.var persons = new [] {
new { Name = "John", Age = "23"},
new { Name = "Mary", Age = "21"},
new { Name = "Joan", Age = "24"},
new { Name = "Tom", Age = "24"},
new { Name = "Hank", Age = "22"},
new { Name = "Steve", Age = "22"},
new { Name = "Bella", Age = "22"},
};
Console.WriteLine(
persons.GroupBy(p => p.Age)
.OrderBy(p => p.Key)
);
In order to order a list by more than one field you can easily add a .ThenBy() to the end of the chain. Something to note here though is that the actual sequence's order is not changed by these commands. Most operations done in LINQ have been made to be side effect free. These operations just make copies of the sequence, make changes and pass it along.
There are some points to remember when deciding to write your own extension methods. Just like implicit typing there are pros and cons to defining extension methods. You have to realize that the code that you write in a group development environment is different than code you write for yourself. You now have to know your audience and your maintainers. You don't want to surprise any of the people working with the code. Extension methods could be really confusing for someone who's not familiar with them. They can also cause all kinds of problems. For example if you defined a method which is added to the framework in the next version, your code will break. Unless your lucky enough to get the exact same implementation of the method added. You can also cause a lot of problems if you don't define the extension methods in the right namespace. You definitely don't want to get all the extension methods of IEnumerable in your intellisense suggestions if you're not using them? Just think long and hard about defining your own extension methods and put them in the right namespace and with the right name. It may be worth it to standardize your extension methods naming scheme so that every person in the group knows it when they're calling an extension method.
Well...this is about it ! Guess what? You now know LINQ ! You just don't know that you do yet. LINQ is just syntactic sugar over all we have learned so far. It only allows a more familiar syntax for queries. Everything will fall into place with the next post which is officially about LINQ containing query expressions and LINQ to objects.
Stay tuned !