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;

});

Published 22 June 04 12:33 by kcwalina
Filed under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Eric Gunnerson's C# Compendium said on June 22, 2004 1:18 PM:
# Herb said on June 22, 2004 2:00 PM:
Finally! You can almost smell the STL from here. The use of anonymous delegates is further icing on the cake at this point.
# Eric J. Smith's Weblog said on June 22, 2004 2:25 PM:
# Michael Earls said on June 22, 2004 2:51 PM:
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.
# mike said on June 22, 2004 3:13 PM:
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? ;)
# damien morton said on June 22, 2004 3:23 PM:
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>.
# Taylor Wood said on June 22, 2004 3:37 PM:
This is awesome. I have already thought of 10 instances I can use this in, so hurry the f%@* up!
# David A. Mellis said on June 22, 2004 4:01 PM:
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?
# Joe White's Blog said on June 22, 2004 4:06 PM:
# damien morton said on June 22, 2004 4:49 PM:
also on the subject of Enumerable<T>

I want to see operator+(Enumerable<T>, Enumerable<T>) which will concatenate two sequences
# damien morton said on June 22, 2004 5:11 PM:
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;
}
# gb said on June 22, 2004 6:16 PM:
wow, C# is becoming ruby, I'm sure if the .net guys quoted it they would get lot of feedback ;)
# Sean said on June 22, 2004 7:52 PM:
Its just a pity the anonymous delegate syntax sucks.
# Cook Computing said on June 23, 2004 3:39 AM:
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...
# Ryan Heath said on June 23, 2004 3:39 AM:
Wow, this is cool.
Much nicer and cleaner than the use of those (almost silly) functor objects icw STL in c++ ;)
// Ryan
# -- said on June 26, 2004 11:22 AM:
brain. hurts.
# Michael said on June 28, 2004 2:04 AM:
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...

# Ryan Lamansky (Kardax) said on June 28, 2004 8:13 PM:
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.
# Cyrus Najmabadi said on July 1, 2004 4:00 AM:
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?
# Thong Nguyen said on July 4, 2004 3:15 AM:
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?
# lukester said on July 7, 2004 3:01 PM:
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#.
# Jb in a nutshell said on July 12, 2004 5:49 AM:
# Jb in a nutshell said on July 12, 2004 5:49 AM:
# James Bellinger said on July 18, 2004 10:37 PM:
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.. :)
# BCLTeam's WebLog said on July 29, 2004 4:41 PM:
# BCLTeam's WebLog said on July 29, 2004 4:44 PM:
# Zohar said on July 31, 2004 6:05 AM:
Great Work.

This will make the platform a much better place.

Ho about applying this to more areas :

File.AsText("MyFile.txt").EachLine(delegate( line string){
// do something with line
});


same for db's etc.

this also means you can wrap the usage internaly with a using block, and save the users the need to remember to manage the underlying resource....


# Peter Golde said on August 7, 2004 4:39 PM:
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

# ThoughtChain said on August 29, 2004 4:39 PM:
# Massimo Prota's BLog said on September 15, 2004 10:52 AM:
# Joe Walnes said on September 16, 2004 3:39 PM:
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...
# GarethJ's WebLog said on October 4, 2004 9:54 PM:
# Objects, Systems and Everywhere In-Between said on February 2, 2005 4:49 PM:
# Steve Eichert said on April 26, 2005 10:12 PM:
What are closures?
# Indigio Blog - Colorado’s premier Interactive Agency blog - specializing in SEO, Creative Design, Development and Analytics » Blog Archive » Ruby On Rails: First Impressions said on December 10, 2007 2:03 AM:

PingBack from http://blog.indigio.com/index.php/2007/12/10/ruby-on-rails-first-impressions/

# zdzislaw (jesse) Cwalina, MI said on February 26, 2008 10:59 PM:

Krzysztof you are coming from noble family in poland from region of kurpie and have a "herb" godziemba.png

Zjesse123@yahoo.com

# kcwalina said on February 27, 2008 2:32 PM:

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? :-)

Leave a Comment

(required) 
(optional)
(required) 
Page view tracker