Share via


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 sequences

  • Anonymous
    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 removed

  • Anonymous
    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++ ;)
    // Ryan

  • Anonymous
    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 removed

  • Anonymous
    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 -> FindWhenTrue

  • Anonymous
    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.com

  • Anonymous
    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/