To continue ChalamiuS’ theme of C# goodness, let’s explore a feature of C# introduced in version 2 of the CLS: the anonymous method. Along the way we’ll also encounter Generics and Object Initializers, but they are not the focus of this post.
Anonymous methods and other features of C# 2.0 and 3.0 incorporate paradigms from functional programming and other (relatively) exotic notions. The full implications of this are and the theory behind it are only minimally covered in this post. It should also be noted that in C# 3.0, lambda expressions were introduced, which work similarly to anonymous methods and look a whole lot cooler.
What is an anonymous method?
To answer this question, let’s first define what a named (eg, not anonymous) method is, using Object.ToString() as an example:
- A method name.
- A return type. Object.ToString, unsurprisingly, returns a string. Note that void is a type.
- A parameter list. Object.ToString does not take any parameters.
- A method body. The default Object.ToString implementation returns the type name.
Without all of these elements, we cannot have a method. Let’s similarly analyze the makeup of an anonymous method:
- A return type.
- A parameter list.
- A method body.
The major difference is, of course, the lack of a method name. Another significant difference not apparent in our comparison is that the return type of parameter list of an anonymous method is declared in a delegate. Any number of anonymous methods may conform to the same delegate. This is a very basic look at anonymous methods. I suggest you read more by people who actually know what they’re talking about.
Why use an anonymous method?
There are any number of reasons to use an anonymous method instead of a regular named method, but my favorite is to help organize small blocks of specialized code. Instead of declaring a very short method and then calling it from only one place in your code, you can put the anonymous method code right inside the method that needs it. Anonymous methods are also a great way of sharing information between blocks of code without having use classes or complex method declarations.
We must be careful to not declare anonymous methods when the code they contain may be used in more than once place. While this can be fixed by refactoring the anonymous method into a named method, it’s best to avoid this pitfall in the first place.
Wait, what?
Instead of drowning in theory and technical arcana, let’s see a (highly contrived) example. In this example, we create a list of cars and print out any car meeting a certain criteria. The code declaring the Car class and filling the list of cars is at the end of this post.
Example without anonymous method
static void Main(string[] args) { List cars = CreateCars(); foreach (Car car in cars) { if (DoesCarMeetCriteria(car)) Console.WriteLine(car.ToString()); } } static bool DoesCarMeetCriteria(Car car) { return car.Manufacturer == "Toyota" || car.Manufacturer == "Nissan" && car.Turbocharged == false; } |
Example with anonymous method
static void Main(string[] args) { // Create a list of cars meeting criteria by calling the FindAll() // method of the List returned by CreateCars(). List cars = CreateCars().FindAll(delegate(Car candidate) { // Return whether the car matches our criteria. FindAll() // expects our anonymous method to return a boolean, and // the compiler makes sure this is the case. return candidate.Manufacturer == "Toyota" || candidate.Manufacturer == "Nissan" && candidate.Turbocharged == false; }); foreach(Car car in cars) { Console.WriteLine(car.ToString()); } } |
Conclusion
Which do you prefer? I like the anonymous method; while the syntax may seem strange it becomes quite natural after using it a few times. Sadly this example is somewhat weak; using an anonymous method doesn’t really gain us anything. However, showing the true power would touch on language design paradigms and the like, which I am avoiding in this post.
Here’s the rest of the code required to compile and run the example:
The Car class
class Car { public string Manufacturer, Model; public bool Turbocharged; public override string ToString() { return String.Format( "{0} {1} ({2}Turbo)", Manufacturer, Model, Turbocharged ? "" : "Non-"); } } |
Creating the Car list
static List CreateCars() { List cars = new List(); cars.Add(new Car() { Manufacturer = "Toyota", Model = "AE-86 Trueno GT-APEX", Turbocharged = false }); cars.Add(new Car() { Manufacturer = "Toyota", Model = "MR2", Turbocharged = false }); cars.Add(new Car() { Manufacturer = "Nissan", Model = "180SX", Turbocharged = false }); // Some models are turbo-charged. cars.Add(new Car() { Manufacturer = "Nissan", Model = "Sileighty", Turbocharged = true }); return cars; } |