Sunday, 29 November 2009

Solving Delegate Variance

Post redacted. It turns out I was making a meal out of something silly - converting from (for example) an EventHandler delegate-instance to a MouseEventHandler delegate instance.

I did it the hard way; Barry Kelly has the right idea (see comments). I bow to greater wisdom.

2 comments:

Barry Kelly said...

I'm not sure what the problem with CreateDelegate is - it supports type-safe variance just fine for me.

However, given a delegate which is variance-compatible with another delegate, and you want to assign it, it is often easier simply to assign the Invoke method instead, rather than go through the rigmarole of CreateDelegate.

The only way I can see you needing a dynamic method as a shim is if the variance you need is not typesafe.

Anyway, here's my test program to see what I mean:

using System;

class A {}
class B : A {}

delegate void DA(A a);
delegate void DB(B b);

class App
{
    static void Foo(A a)
    {
    }
    
    static T CastDelegate<T>(Delegate src)
        where T: class
    {
        return (T) (object) Delegate.CreateDelegate(
            typeof(T),
            src.Target,
            src.Method,
            true); // throw on fail
    }
    
    static void Main()
    {
        DA a = Foo; // works fine
        DB b = Foo; // works fine
        
        b = a.Invoke; // the easy way to assign delegate using variance
        // adds layer of indirection though
        
        b = CastDelegate<DB>(a); // the hard way, avoids indirection
    }
}

Marc Gravell said...

Odd - my comment evaporated! Repeat... @Barry - thanks, very interesting. I wonder if I've been missing a trick. I'll investigate...