PDC05: Integrated query and C# 3.0
The big news for C# developers at PDC has definitely been Language Integrated Query (the LINQ project) and the new features in C# 3.0, and the largest room in the convention centre was packed, with an overflow session scheduled for tomorrow for those that didn’t make it in time.
There’s far too much to describe in a blog post, but the headline features of version 3.0 include lambda expressions, extension methods (a little like mixins in Ruby), anonymous types, and local type inference. A lot of these are designed to support the new LINQ features, which make it possible to write query-like expressions in C# 3.0 or VB9 code. Queries can be run against anything that implements IEnumerable, which means you can query arrays with where clauses, group by, and order the results however you want.
The really great thing is that all of this is strongly-typed – Anders and his team have managed to take some of the most compelling features of dynamic scripting languages like Ruby and Python and deliver them in C# along with compile-time type checking and IntelliSense.
The syntax takes some getting used to:
Customer[] customers = GetCustomers();
var results =
from c in customers
where c.City == “London”
select new { FullName = c.FirstName + “ “ + c.LastName, c.CompanyName }
This little snippet shows a number of things: first there’s the the var keyword, which is not a variant, but rather tells the compiler to infer the type of the results local variable. Writing “var i = 3” for example, is exactly the same as “int i = 3”, and this shorthand becomes useful later in the statement.
Most of the rest of the statement is really syntactic sugar for a number of regular method calls. In fact the statement could be written in C# 2.0 as something like:
private class CustSummary { ... }
IEnumerable results = customers
.Where(delegate(Customer c) { return c.City == “London” })
.Select(delegate(Customer c) {
return new CustSummary(c.FirstName + “ “ + c.LastName, c.CompanyName)
});
If you haven’t tried out generics and anonymous delegates in C# 2.0, the code above is going to be just as impenetrable as the 3.0 version, and as Anders pointed out, all of the features planned for 3.0 make heavy use of the innovations in the 2.0 release of the CLR. In fact, C# 3.0 does not require a new version of the CLR – everything works on VS 2005 today once you install the LINQ Customer Technical Preview.
There are a couple of things about this however that are difficult to express in version 2.0. First of all, where did the CustSummary class come from? In fact, CustSummary is an anonymous type, created just to support the statement. It’s a real type, defined in your assembly, and you will have full IntelliSense support for its members, you just can’t refer to it by name since it is generated by the compiler (so it’ll actually be called type0001 or something). This is why we used the var keyword to define the results local – this way the compiler can define results correctly for us, since it knows the name of the anonymous type.
The other strange thing about the code is the Where and Select methods. Where did they come from? We seem to be calling them on the Customer array, but they aren’t part of the Array class. In fact these are extension methods, which have been added to everything in the current scope that implements IEnumerable, though they aren’t part of the IEnumerable interface. You get to use the extension methods simply by adding a using reference to System.Query, which includes a class that defines the extra methods.
Extension methods are a little scary since they allow you to add methods to any predefined class. By using a special syntax you can define these as static methods in a separate class, but make them available on the class they extend. Want to add a ToTitleCase() method to all strings? No problem. Add ToBase64() to all byte arrays? Easy. Anders demonstrated adding a ToXml method to IEnumerable, which created an XML stream for all arrays, lists and anything else that implements that interface. Powerful stuff, though as he made clear, the potential for abuse is frightening :)
If you want to find out more about these features, the best place to start is the LINQ technical preview, available on the MSDN site. All the source code for the extension methods is included as well, so you can dive straight in and start playing.