Delegates, Lambdas, Type Inference and Long Playing Records

Delegates, Lambdas, Type Inference and Long Playing Records

Rate This
  • Comments 3

Today is my 33 1/3rd birthday! I'm one third of the way through my first century. I feel like I should go buy some LPs to celebrate, not that I have anything that will play them.

In other news, someone sent me this code the other day. Suppose you've got a collection of mammals and you want to feed all the giraffes:

delegate void Action<X>(X x);
class MyCollection<T> : Collection<T> where T : class {
  public void Apply<S>(Action<S> action) where S : class, T {
    foreach ( T t in this ) {
      S s = t as S;
      if (s != null )
        action(s);
    }
  }
}
//...

MyCollection<Mammal> mammals = new MyCollection<Mammal>();
//...
mammals.Apply<Giraffe>(delegate(Giraffe g){Feed(g);});

This works just fine. The questioner was wondering why this didn't work:

mammals.Apply(delegate(Giraffe g){Feed(g);});

That is, why do we need to specify the type parameter to the generic method? Shouldn't type inference take care of it?

That would be nice, but unfortunately section 26.6.4 of the specification clearly states that nothing is inferred from any anonymous method, null, or method group argument.

In the next version of C# we will have a new, more powerful, more flexible kind of anonymous method called a lambda expression. A lambda expression is like an anonymous method with additional type inferencing features and a more natural syntax than the clunky delegate syntax.

Unfortunately for the questioner, the new lambda type inferencing rules actually will not help with this scenario. However, the new type inferencing rules will apply in any scenario where "classic" type inferencing can be used to determine the generic type of a lambda argument, and from that, determine the generic type of a lambda return, and from that, the generic return type of a delegate, and from that, a method type parameter.

That last sentence probably made no sense. Let's look at an example where the new type inferencing rule would apply.

Suppose you have a collection of Customers and you want to create a collection of their names:

delegate R Func<A, R> (A arg);
IEnumerable<S> Select<T, S>(IEnumerable<T> source, Func<T, S> selector) {...}
//...
names = Select(customers, c => c.Name);

With the old type inferencing rules we can infer that T is Customer, and we would infer nothing from the lambda. However, in C# 3.0 we will enter a second round of type inferencing, where we'd note that we hadn't inferred anything from the lambda yet, and that it is being converted to a partially inferred delegate type Func<Customer, R> requiring type inference on the return type.

We now can infer that the parameter to the lambda, c must be of type Customer, and therefore the return type of the lambda must be the type of Customer.Name, which is string. Therefore the delegate type is Func<Customer, string>, and hey,we've managed to infer the types of all the parameters to Select<T, S>, so we're done.

Cool eh? Now all we've got to do is actually implement it...

  • Not that I don't get the concepts, but why did you gratuitously switch from the Giraffe example to another one when showing the the lambda way?  Why not code the same problem in lambdas to clearly show the delta?
  • Hmm, I probably should have made this point more clear.  

    The new lambda type inferencing feature actually would NOT help the initial example -- the initial example was simply what got me thinking about the problem space.  

    The reason why it would not help is because the new type inferencing rules only allow for additional inferences to be made on delegate _return_ types, not on delegate _parameter_ types.  Once the delegate return type has been inferred, that fact may be used to infer the method parameter type.

    However, Apply( (Giraffe g)=>Feed(g) ); would not infer that S is Giraffe.

    Perhaps I ought to rewrite this article to use the select example throughout.

    Thanks!
  • I have another question about C# generics that nobody seems to be able to answer. Personally, I'm very impressed with the new C# generic features, especially considering how long it took for most compilers to get C++ templates right (many of them still aren't quite there). Anyway, here's my question:

    Is there any way in C# to generically call a constructor without resorting to reflection? What I mean is this: if I declare an interface or a base class, I can specify a specific function that will be available in all instances of that interface or base class. Then I can write something like:

    public void foo(T x) where T: MyInterface {
     x.myFunction();
    }

    So how do I do that, if I want to call a constructor like that. Basically something like this (this doesn't compile in C#):

    public interface IMyInterface {
     IMyInterface(string AValue);
    }

    public class MyClass : IMyInterface {
     public MyClass(string AValue) { Console.WriteLine("MyClass.Constructor with {0}", AValue); }

     public static void CreateMyInterface<T>(string AValue) where T: IMyInterface {
       return new T(AValue);
     }

     public static void Main() {
       IMyInterface myInterface = CreateMyInterface<MyClass>("42");
       // should call MyClass' constructor and return a new MyClass object
       // console output: "MyClass.Cconstructor with 42"
     }

    }


    Basically, it's a type of virtual constructor. Any ideas how to do that? My workaround has been to create static functions in each one of these classes and pass a delegate for this static function around but that's not really very convenient or safe. Reflection is another option but also fraught with significant performance problems. What's your take?

Page 1 of 1 (3 items)