views:

80

answers:

4

There are two descriptions of the delegate: first, in a third-party assembly:

public delegate void ClickMenuItem (object sender, EventArgs e)

second, the standard:

public delegate void EventHandler (object sender, EventArgs e);

I'm trying to write a method that will receive a parameter of type EventHandler and will call third-party library, with the parameter ClickMenuItem.

How to convert the ClickMenuItem to EventHandler?

+4  A: 

EDIT: There's a fourth option, i.e. to avoid all this nonsense and do what Jon Skeet suggests in his answer.

Something like this?

public static EventHandler ToEventHandler(this ClickMenuItem clickMenuItem)
{
    if (clickMenuItem == null)
        return null;

   return (sender, e) => clickMenuItem(sender, e);
}

and the reverse:

public static ClickMenuItem ToClickMenuItem(this EventHandler eventHandler)
{
   if (eventHandler == null)
       return null;

   return (sender, e) => eventHandler(sender, e);
}

Note that the compiler infers which delegate-types to convert the lamda-expressions to.

EDIT: If you prefer, you can use anonymous-delegates too.

EventHandler eventHandler =  delegate(object sender, EventArgs e)
                             { 
                                clickMenuItem(sender, e); 
                             };
return eventHandler; // can be inlined, type-inference works fine

The third alternative of course, is to write a closure-class yourself. I wouldn't really recommend this, but it gives you an idea of what the compiler does with the anonymous methods. Something like:

public static class ClickMenuItemExtensions
{
    public static EventHandler ToEventHandler(this ClickMenuItem clickMenuItem)
    {
        if (clickMenuItem == null)
            return null;

        // new EventHandler not required, included only for clarity 
        return new EventHandler(new Closure(clickMenuItem).Invoke);
    }

    private sealed class Closure
    {
        private readonly ClickMenuItem _clickMenuItem;

        public Closure(ClickMenuItem clickMenuItem)
        {
            _clickMenuItem = clickMenuItem;
        }

        public void Invoke(object sender, EventArgs e)
        {
            _clickMenuItem(sender, e);
        }
    }
}
Ani
What is "=>"? Please give a link to MSDN. I can not find.
SkyN
@SkyN: That's the "goes to" operator, which is used to write a lamda-expression. http://msdn.microsoft.com/en-us/library/bb397687.aspx
Ani
@Ani: Why bother with all of this when you can just convert directly, as per my answer?
Jon Skeet
@Jon Skeet: I bow to your wisdom, sir. :)
Ani
+6  A: 

Fortunately, it's simple. You can just write:

ClickMenuItem clickMenuItem = ...; // Wherever you get this from
EventHandler handler = new EventHandler(clickMenuItem);

And in reverse:

EventHandler handler = ...;
ClickMenuItem clickMenuItem = new ClickMenuItem(handler);

This will even work in C# 1.0. Note that if you then change the value of the original variable, that change won't be reflected in the "converted" one. For example:

ClickMenuItem click = new ClickMenuItem(SomeMethod);
EventHandler handler = new EventHandler(click);
click = null;

handler(this, EventArgs.Empty); // This will still call SomeMethod
Jon Skeet
Thinking about it, this would avoid a level of indirection as well wouldn't it? The new `Delegate` instance would have the same `Target` and `Method` as the original?
Ani
@Ani: I'm not sure. I *think* it actually maintains the original delegate, so it's just a simpler way of doing your lambda expression version. I'm not entirely sure though.
Jon Skeet
A: 

You may like to checkout Variance in Delegates.

.NET Framework 3.5 and Visual Studio 2008 introduced variance support for matching method signatures with delegate types in all delegates in C# and Visual Basic. This means that you can assign to delegates not only methods that have matching signatures, but also methods that return more derived types (covariance) or that accept parameters that have less derived types (contravariance) than that specified by the delegate type. This includes both generic and non-generic delegates.

KMan
That's odd... I could have sworn this was *actually* in C# 2, not C# 3.
Jon Skeet
For example, see http://en.csharp-online.net/New_Features_in_CSharp_2.0%E2%80%94Gain_Flexibility_with_Delegate_Covariance_and_Contravariance - looks like MSDN is wrong...
Jon Skeet
@Jon: *MSDN is wrong* (0:
KMan
A: 

In addition to other answers, if you want to do convert between compatible delegate types without knowing the type at compile time, you can do something like that:

static Delegate ConvertDelegate(Delegate sourceDelegate, Type targetType)
{
    return Delegate.CreateDelegate(
            targetType,
            sourceDelegate.Target,
            sourceDelegate.Method);
}

It can be useful if you need to subscribe to an event dynamically.

Thomas Levesque