Last time, I wrote about the conciseness of C# 3.0 lambda expressions being a distinct advantage over C# 2.0 anonymous methods. This time, I'm going to look at something a bit more subtle that, in some situations, helps us write more elegant code if lambda expressions are used instead of anonymous methods. But first, I need to talk about generic type inference.
The idea of generic type inference is this: when calling a generic method (that is, a method that defines its own generic type parameters) you can leave off the generic type arguments and the compiler will try to infer what they should be based on the parameters that are passed to the method. For example, assume that I have a generic method like this:
Naturally, I can call the method with code like this:
However, I could also call the method without specifying the generic type argument and allow the compiler to infer it:
Because an array of ints is being passed into the Display<T> method and that array implements IEnumerable<int>, the compiler is able to determine that "T" should be replaced with "int". This is a fantastic feature because it allows us to write code that isn't littered with angle brackets.
Now, consider the code with anonymous methods from my last post:
If we allow the compiler to infer the types of the generic parameters, the code can be rewritten like this:
Cool. OK, let's look at one last example.
An extremely useful generic method on the Array class is Array.ConvertAll<TInput, TOutput>. This essentially allows you to take an array of one type and convert it to an array of another type. Or, you could convert an array to a new array of the same type and just perform some sort of transformation on the elements of the array. Here's the method signature:
Here's the signature of the Converter<TInput, TOutput> delegate type it uses:
And, here's some C# 2.0 code that demonstrates this handy method by converting an array of ints to an array of strings:
Declaring the generic type arguments when calling this method is clunky and takes up a fair bit of code real estate (13 characters in this example). Fortunately, we can remove those type arguments and let the compiler infer them, right? Wrong. It turns out that this will result in a compiler error:
Try compiling that code and you'll get this error: "The type arguments for method 'System.Array.ConvertAll<TInput,TOutput>(TInput[], System.Converter<TInput,TOutput>)' cannot be inferred from the usage. Try specifying the type arguments explicitly." What's going on here? The problem is with the "TOutput" generic type parameter.
Look careful at the definition of the ConvertAll method and the Converter delegate type. "TInput" is easy for the compiler to infer because it is used directly in a parameter to ConvertAll. However, "TOutput" is only used as the return type of the Converter delegate and, unfortunately, the C# compiler ignores anonymous methods when inferring types. So, the type inference fails and we are forced to use less-than-ideal code.
Fortunately, lambda expressions are treated differently than anonymous methods by the C# compiler's generic type inference algorithm. If you want to see the details of how this algorithm works (which are fascinating), check out a video of Eric Lippert explaining them here.
If we simply convert our anonymous method to a lambda expression, we get a successful compile:
Concise. Elegant. Cool.
Lambdas: 2Anonymous Methods: 0
Page rendered at Monday, May 20, 2013 10:53:23 AM (Pacific Standard Time, UTC-08:00)
If feel a bit behind and need to catch up on WPF, this is the book.
Great book on F# containing from Beginner to Advanced. It even has chapters on more arcane features of the language, such as Computation Expressions and Quotations.
Because this book provides source code in Standard ML, it's a fantastic resource for learning F#. One bit of warning: this book does not teach classic data structures. While structures such as binomial heaps and red-black trees are presented, it is assumed that the reader already knows and understands them.
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.