DLinq Extension Methods Decomposed by ThinqLinq

DLinq Extension Methods Decomposed

A recent inquiry on the DLinq forum (http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=355342&SiteID=1) asked why the .ToArray method was not being recognized in DLinq. Actually, the issue they were seeing was a message that their array object (db.Customers) didn’t have a .ToArray method. While the compiler is correct, it does not point to the true source of the problem. This is due to the magic of the Extension Methods. The sample that was in question is as follows:

Northwind db = new nwind.Northwind("C:\\projects\\data\\northwnd.mdf");
db.Log = Console.Out;
var results1 = from c in db.Customers
where c.City == "London"
orderby c.CompanyName
select c.CompanyName;
var aResult = results1.ToArray();
ObjectDumper.Write(aResult);

Here the .ToArray method is an Extension Method. What was missing is the using clause (imports in VB) for System.Query.Sequence. Typically, this is set up as a global import using the templates that come with the LINQ preview code. Below is a version of the same query using dot notation which calls attention to the methods being used.

var results2 = db.Customers
.Where(c=>c.City == "London")
.OrderBy(c => c.CompanyName)
.Select(c => c.CompanyName)
.ToArray();
ObjectDumper.Write(results2);

In this version, the .Where, .OrderBy, .Select and .ToArray methods are all extension methods which extend IEnumerable<T>. An extension method has the following signature:

public shared IEnumerable<T> AnExtension(this IEnumerable<T> source, object AnOperator) {}

Here the first parameter is an instance of the object that is being extended. Via a syntactic sugar trick, the compiler when trying to resolve a method will look through the direct methods that an object implements. If it can not find one that is appropriate, it will search through the methods with the “this” keyword modifying the first parameter and interject the object as the source without it being explicitly specified. Given the above method declaration, the following are evaluated the same:

IEnumerable<Customer> Cust = db.Customers;
var x = AnExtension(Cust, Null);
// The following is identical
var y = Cust.AnExtension(Null)

Given this implementation of the extension method, the original query can be re-written as below. In this case, I have fully qualified the namespaces to be precise. Here, I am nesting the db.Customers, which is a IEnumerable<Customer> object with 4 extension methods. Hopefully the indenting will aid in deciphering the construction of the assignment.

var results3 = System.Query.Sequence.ToArray(
System.Query.Sequence.Select(
System.Query.Sequence.OrderBy(
System.Query.Sequence.Where(db.Customers
, c => c.City == "London")
, c => c.CompanyName)
, c => c.CompanyName)
);
ObjectDumper.Write(results3);

What should become clear is that the query syntax and dot notation are more readable (and likely maintainable) than the nested, but fully qualified version. Hurray for extension methods! Be careful with them however. The extension method is a big gun that can end up shooting you when you least expect it. Also, before someone asks, extension methods are not a way of doing multiple inheritance. All extension methods must be static (shared) and not maintain state.

Posted on - Comment
Categories: LINQ -
comments powered by Disqus