Mar 19

Generic interfaces and delegates

Posted in Basics C# CLR      Comments Off on Generic interfaces and delegates

In the previous post, I’ve introduced the concept of generics. In this post, we’ll keep looking at generics and we’ll talk a little bit about how we can create generic interfaces and delegates. Without generic interfaces, manipulating a value type through its interface reference would result in boxing (and less compile type safety). The some goes for delegates: with generics, it’s possible to create a delegate which allows a value type instannce to be passed back to a callback method in a type-safe way without any boxing.

There’s not much to say about the syntax used for creating generic interfaces and delegates. So, here’s an of a generic interface:

public interface IDoSomething<T> {
    void DoItNow(T);
    T Whatgetdate();

And here’s an example of a generic delegate (btw, it’s defined by the framework):

public delegate void Action<T>(T item);

Tipically, the compiler will transform a delegate into a class which looks like this:

public class Action<T> : MulticastDelegate {
    public Action(Object obj, IntPtr method);
    public virtual void Invoke(T item);
    public virtual IAsyncResult BeginInvoke(T item, AsyncCallback cb, Object obj);
    public virtual void EndInvoke(IAsyncResult result);

(And we’ll see why in future posts Smile)

From .NET 4.0 onward, the type arguments of a generic delegate or interface can be classified as contravariant or covariant. In previous versions, generic arguments were always invariant, ie, the generic type parameter couldn’t be changed after being defined. In C#, contravariant type arguments are indicated through the in keyword. It means that the generic type parameter can change from a class to another derived from it. On the other hand, covariant type arguments are indicated in C# through the out keyword and, in this  case, a generic type argument can change from a class to one of its  base class. An example might help you understand what’s goin here. Suppose we start with this (again, defined by the framework):

public delegate TResult Func<in T, out TResult>(T arg);

In this case, T is contravariant and TResult is covariant. So, if you have the following classes:

class Base {}
class Derived : Base {}

Now,  if you still haven’t forgotten that Object is the base class of all objects, then from .NET 4.0 onward, you can simply do this:

Func<Object, Derived> fun1 = null;//not important here
Func<Base, Base> fun2 = fun1;

Notice that there’s no cast there! this is not the easiest example to understand what’s going on, but I really don’t have time for more (sorry). What matters is understanding that fun1 references a method which expects and object and returns na instance of Derived. On the other hand, fun2 expects a method that receives a Base and returns another Base instance. Since you can  pass a Base to a method which expects an Object and since you can treat a Derived instance as  if it was a Base instance (because Derived extends Base), then the previous code is safe.

Interestingly, the compiler will only be happy with contravariance and covariance if it can infer that there is a conversion between the tpyes being used. That means that variance won’t work for value types because it would require boxing. In practice, this means that this won’t compile:

void DoSomething(IEnumerable<Object> items) { }
DoSomething(new List<Int32>());//ooops:  compiler error

It’s a pity, but that’s just the way it is. Before ending, there’s still time to say that you should always specify the variance of a type argument when creating generic arguments. This doesn’t introduce any nasty side effects and enables the use of the delegate in more scenarios. If you want to know more about variance, then I recommend reading Eric Lippert’s excellent series on the topic. And that’s it for now. Stay tuned for more.