I recently saw this question regarding the use of methods that get passed delegates — specifically, the .NET 2 collection classes make use of this, like for example the List<T>
class, which takes a Predicate<T>
as a parameter to its Find
method. Now the question is how to make use of this method? How does the delegate know what to do when it’s called?
As I see it, there are two (good) approaches to this: using an anonymous method and using a separate class. I’m going to illustrate both approaches below, with a sample that makes a few assumptions:
- You have a class called
MyObject
that has a property calledId
, of typeGuid
. - You have a list of MyObject instances, declared
List<MyObject> list = new ...
, which contains a few instances. - Now you want to write a method called
FindById
, that takes aGuid
and makes use of the methodList<T>.Find
to find aMyObject
in the list which has the given Id.
Anonymous methods
I’m not going to explain the complete concept of anonymous methods in this article. Suffice it to say that they can be used in places where delegates would otherwise be expected in .NET, but without the overhead of having to create a named method to pass in to the delegate’s constructor. Using an anonymous method, you could write your FindById
method like this:
MyObject FindById(Guid searchId) {
return list.Find(delegate(MyObject myObject) {
return myObject.Id == searchId;
});
}
As you can see, an anonymous method is created that has the exact signature of the Predicate<T>
delegate, getting passed a MyObject
and returning a bool. This anonymous method has direct access to the searchId that was passed in to FindById, so a comparison is easily possible.
A separate class
The worst drawback of the anonymous method approach is that it’s not easily reusable. If you need a similar comparison algorithm in another place, you’ll have to write the complete code again. This may not be a problem in the sample, but if the code were a bit more complex it wouldn’t be a good idea. So why am I talking about a separate class? Because the value that’s needed for the comparison needs to be stored somewhere, and this shouldn’t be done in the same class that defines the FindById method — it’s much cleaner (and easier to make thread-safe) to put the value into a separate class. Of course, this class could be within the other, so it won’t polute your namespace. So the code for that extra class and the FindById
method could look like this:
class MySearchByIdClass {
Guid searchId;
public MySearchByIdClass(Guid searchId) {
this.searchId = searchId;
}
public bool PredicateDelegate(MyObject myObject) {
return myObject == searchId;
}
}
MyObject FindById(Guid searchId) {
return list.Find(new Predicate<MyObject>(
new MySearchByIdClass(searchId).PredicateDelegate));
}
Obviously the coding overhead is a bit higher here, but then we get complete reusability in return. The FindById
method simply creates an instance of the encapsulation class to store the search parameter and uses that class’s delegate implementation to pass in to the Find
method.