【问题标题】:How to convert delegate to identical delegate?如何将委托转换为相同的委托?
【发布时间】:2011-05-01 17:27:10
【问题描述】:

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

public delegate void ClickMenuItem (object sender, EventArgs e)

二、标准:

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

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

如何将 ClickMenuItem 转换为 EventHandler?

【问题讨论】:

标签: c# delegates type-conversion


【解决方案1】:

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

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

【讨论】:

  • 考虑一下,这也可以避免一定程度的间接性,不是吗?新的Delegate 实例将与原始实例具有相同的TargetMethod
  • @Ani:我不确定。我认为它实际上维护了原始委托,因此它只是一种更简单的 lambda 表达式版本。不过我不完全确定。
  • @Ani 查看生成的 IL 代码可以看到,新委托是对旧委托的 Invoke 方法的调用...
【解决方案2】:

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

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

如果您需要动态订阅事件,它会很有用。

【讨论】:

    【解决方案3】:

    编辑:还有第四种选择,即避免所有这些废话,并按照 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);
            }
        }
    }
    

    【讨论】:

    • 什么是“=>”?请给 MSDN 的链接。我找不到。
    • @SkyN:那是“goes to”运算符,用于编写 lamda 表达式。 msdn.microsoft.com/en-us/library/bb397687.aspx
    • 按照我的回答,当您可以直接转换时,为什么还要打扰所有这些?
    • @Jon Skeet:先生,我向您的智慧低头。 :)
    【解决方案4】:

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

    会失败。

    【讨论】:

      【解决方案5】:

      您可能想结帐Variance in Delegates

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

      【讨论】:

      • 这很奇怪......我可以发誓这是在 C# 2 中,而不是在 C# 3 中实际上
      • 例如,见en.csharp-online.net/… - 看起来MSDN是错误的......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-05
      相关资源
      最近更新 更多