Delegate-Based APIs
Generics and Anonymous Methods/Delegates make up a powerful pair that can be used to create elegant query APIs. Here are some that we just added to the .NET Framework’s Collection libraries. I used List<T> to illustrate the APIs but most of those were also added to System.Array.
API Design
Delegates
namespace System {
public delegate void Action<T>(T obj);
public delegate bool Predicate<T>(T obj);
public delegate U Converter<T,U>(T from);
public delegate int Comparison<T>(T x, T y);
}
List<T>
public class List<T> : … {
public int FindIndex(Predicate<T> match);
public int FindIndex(int index, Predicate<T> match);
public int FindIndex(int index, int count, Predicate<T> match);
public int FindLastIndex(Predicate<T> match);
public int FindLastIndex(int index, Predicate<T> match);
public int FindLastIndex(int index, int count, Predicate<T> match);
public List<T> FindAll(Predicate<T> match);
public T Find(Predicate<T> match);
public T FindLast(Predicate match);
public bool Exists(Predicate<T> match);
public bool TrueForAll(Predicate<T> match);
public int RemoveAll(Predicate<T> match);
public void ForEach(Action<T> action);
public void Sort(Comparison<T> comparison);
public List<U> ConvertAll<U>(Converter<T,U> converter);
}
Finding Even Integers in List<T>
List<int> integers = new List<int>();
For(int i=1; i<=10; i++) integers.Add(i);
List<int> even = integers.FindAll(delegate(int i){
return i%2==0;
});
Finding Complex Type in List<T>
public class Order {
public Order(int number, string item) { … }
public int Number { get { return number; } }
public string Item { get { return item; } }
…
}
List<Order> orders = new List<Order>();
int orderNumber = 10;
Order order = orders.Find(delegate(Order o){
return o.Number==orderNumber;
});
Computing Sum of Integers in List<T>
List<int> integers = new List<int>();
for(int i=1; i<=10; i++) integers.Add(i);
int sum;
integers.ForEach(delegate(int i){ sum+=i; });
Sort Orders in List<T>
List<Order> orders = new List<Order>();
orders.Add(new Order(10,”Milk”));
orders.Add(new Order(5,”Cheese”));
orders.Sort(delegate(Order x, Order y){
return Comparer<int>.Default.Compare(x.Number,y.Number);
});
Convert Orders to Order Numbers
List<Order> orders = new List<Order>();
orders.Add(new Order(10,”Milk”));
orders.Add(new Order(5,”Cheese”));
List<int> numbers = orders.ConvertAll(delegate(Order x){
return o.Number;
});
Comments
Anonymous
June 22, 2004
Finally! You can almost smell the STL from here. The use of anonymous delegates is further icing on the cake at this point.Anonymous
June 22, 2004
Sweet. This is awesome. I agree with Herb. That's one of the things I missed most about C++ when switching to C#... No STL.
Not only is the STL coming back, but it's coming back with added bonuses.
Keep up the awesome work.Anonymous
June 22, 2004
very, very nice to have.
but...
> public List<U> ConvertAll<U>(Converter<T,U> converter);
can't you just call it map(), like everyone else does? ;)Anonymous
June 22, 2004
Many of these methods belong in Enumerator<T> and/or Enumerable<T>. In particular, any of the methods that dont involve an index.
Enumerator<T>/Enumerable<T> should have an implicit conversion operator to List<T>.Anonymous
June 22, 2004
This is awesome. I have already thought of 10 instances I can use this in, so hurry the f%@* up!Anonymous
June 22, 2004
Why did you decide to make these member functions and not static helper functions? Are they part of the IList<T> interface? Will we have to implement this functionality for every list-like object we'd like to supply it for?Anonymous
June 22, 2004
also on the subject of Enumerable<T>
I want to see operator+(Enumerable<T>, Enumerable<T>) which will concatenate two sequencesAnonymous
June 22, 2004
IEnumerable<T> operator+(IEnumerable<T> a, IEnumerable<T> b)
{
foreach (T t in a)
yield return t;
foreach (T t in b)
yield return t;
}Anonymous
June 22, 2004
wow, C# is becoming ruby, I'm sure if the .net guys quoted it they would get lot of feedback ;)Anonymous
June 22, 2004
The comment has been removedAnonymous
June 22, 2004
Krzysztof Cwalina describes some new delegate-based APIs added to the .NET collection libraries. A couple of points came to mind on reading this. One, would it be better if the methods returning List instead returned IEnumerator, using an iterator for...Anonymous
June 22, 2004
Wow, this is cool.
Much nicer and cleaner than the use of those (almost silly) functor objects icw STL in c++ ;)
// RyanAnonymous
June 26, 2004
brain. hurts.Anonymous
June 27, 2004
Ugh - having all these algorithms implemented as member functions is really gross. One of the coolest things about STL is how algorithms have been separated from container definitions.
With the STL method, I can write a new container, and have the existing algorithms function with it. With the method shown here, I have to implement all the algorithms for my container every time I make a new container? That's not cool.
It still has a way to go before reaching the flexibility and elegance of STL...Anonymous
June 28, 2004
Michael:
Anonymous methods are just shorthand for delegates.
Using delegates, you could separate the container from the algorithms with little difficulty. You could even mix delegates with anonymous methods.Anonymous
June 30, 2004
Krzysztof: What is the difference between:
U Converter<T,U>(T t)
and
B Function<A,B>(A a)
Is the latter included in the FX?Anonymous
July 03, 2004
About time!
Are you going to be supporting some of these higher order functions in the string class?
Things like Left(Predicate), Right(Predicate), LeftFromRight(Predicate), RightFromLeft(Predicate), Substring(Predicate, Predicate), TrimLeft(Predicate), TrimRight(Predicate), Convert(Converter), CountChars(Predicate) etc etc etc etc.
My C# class library supports union and intersection of predicates via operator overloading (C# doesn't allow overloading operators on delegate classes but IL does). Are you going to support this?Anonymous
July 07, 2004
Its better to follow the lesson of STL and externalize the algos...
A few comments in here allude to it: define the algorithms as delegates that operate on enumerators and take delegates as arguments, then the container need not worry about adding methods to support these operations.
public delegate T for_each<T>( IEnumerator begin, IEnumerator end, T delegate )
It seems a right and proper STL implementation could now be created for C#.Anonymous
July 18, 2004
Hmm. Any plans to add a Slice method and perhaps a Merge method for arrays? That would be beautiful (the former more important than the latter). In fact, something like a Python-style slice syntax would be beautiful, and maybe + to merge arrays... sorry, dreaming.. :)Anonymous
July 30, 2004
The comment has been removedAnonymous
August 07, 2004
The names for these methods seem a little inconsistent to me. The word "All" seems to me lots of different things.
In "TrueForAll" and "ConvertAll", it appears to mean all the items in the collection.
In "FindAll" and "RemoveAll", it appears to only mean the items that satisfy the predicate.
I would suggest the following renamings:
Find -> FindFirst (for symmetry with FindLast)
FindIndex -> FindFirstIndex
RemoveAll -> RemoveWhenTrue
FindAll -> FindWhenTrueAnonymous
September 16, 2004
Martin Fowler (obligitary Fowlbot namedrop) recently blogged about the power of closures in languages that support them. It's worth remembering that C# 2.0 has true closure support in the form of anonymous delegates. This includes reading and modifying variables outside...Anonymous
April 26, 2005
What are closures?Anonymous
December 09, 2007
PingBack from http://blog.indigio.com/index.php/2007/12/10/ruby-on-rails-first-impressions/Anonymous
February 26, 2008
Krzysztof you are coming from noble family in poland from region of kurpie and have a "herb" godziemba.png Zjesse123@yahoo.comAnonymous
February 27, 2008
Yes, my dad is from Kurpie. I have heard the coat of arms was called "Pniejnia". I even found a nice picture of it on the net with some legend describing the origin. See http://tkwapinski.webpark.pl/herby_p.html#a ... and based on your name I gather we might be a family? :-)Anonymous
October 19, 2008
PingBack from http://esersahin.wordpress.com/2008/10/20/delegate-based-apis/Anonymous
January 14, 2009
PingBack from http://joewalnes.wordpress.com/2004/09/16/the-power-of-closures-in-c-20/