如何将委托转换为相同的委托?

时间:2010-10-28 06:49:32

标签: c# delegates type-conversion

委托有两种描述: 首先,在第三方集会中:

public delegate void ClickMenuItem (object sender, EventArgs e)

第二,标准:

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

我正在尝试编写一个接收EventHandler类型参数的方法,并使用参数ClickMenuItem调用第三方库。

如何将ClickMenuItem转换为EventHandler?

5 个答案:

答案 0 :(得分:25)

幸运的是,这很简单。你可以写:

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

相反:

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

这甚至可以在C#1.0中使用。请注意,如果您随后更改了原始变量的值,则该更改将不会反映在“已转换”的变量中。例如:

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

handler(this, EventArgs.Empty); // This will still call SomeMethod

答案 1 :(得分:6)

编辑:还有第四个选择,即避免所有这些废话并做Jon Skeet在答案中建议的内容。

这样的东西?

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

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

反之亦然:

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

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

请注意,编译器会推断将lamda表达式转换为。

的委托类型

编辑:如果您愿意,也可以使用匿名代表。

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

第三种选择当然是自己写一个封闭类。我不会真的推荐这个,但它让你了解编译器对匿名方法的作用。类似的东西:

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);
        }
    }
}

答案 2 :(得分:5)

除了其他答案之外,如果你想在编译时不知道类型的情况下在兼容的委托类型之间进行转换,你可以这样做:

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

如果您需要动态订阅活动,这将非常有用。

答案 3 :(得分:1)

Thomas Levesque的答案对某些特殊情况不适用。这是一个改进版本。

public static Delegate ConvertDelegate(this Delegate src, Type targetType, bool doTypeCheck)
{
    //Is it null or of the same type as the target?
    if (src == null || src.GetType() == targetType)
        return src;
    //Is it multiple cast?
    return src.GetInvocationList().Count() == 1
        ? Delegate.CreateDelegate(targetType, src.Target, src.Method, doTypeCheck)
        : src.GetInvocationList().Aggregate<Delegate, Delegate>
            (null, (current, d) => Delegate.Combine(current, ConvertDelegate(d, targetType, doTypeCheck)));
}

上述代码的好处是它通过了以下测试

EventHandler e = (o,e)=>{}
var a = e.ConvertDelegate(typeof(Action<object, EventArgs>), true);
Assert.AreEqual(e, e.ConvertDelegate(typeof(EventHandler), true));

同时

EventHandler e = (o,e)=>{}
var a = new Action<object, EventArgs>(e);
Assert.AreEqual(e, new EventHandler(a));

会失败。

答案 4 :(得分:0)

您可以查看Variance in Delegates

  

.NET Framework 3.5和Visual Studio 2008引入了对C#和Visual Basic中所有委托中的委托类型匹配方法签名的方差支持。这意味着您不仅可以为具有匹配签名的方法分配委托,还可以为返回更多派生类型(协方差)或接受具有比委托类型指定的派生类型(逆向)更少的派生类型的参数的方法分配给委托。这包括通用和非通用代理。