How to convert delegate to identical delegate?

后端 未结 5 2053
你的背包
你的背包 2021-02-18 19:04

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

public delegate void ClickMenuItem (object sender, EventArgs e)

s

相关标签:
5条回答
  • 2021-02-18 19:08

    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.

    0 讨论(0)
  • 2021-02-18 19:08

    Thomas Levesque's answer does not work well for some special cases. This is an improved version.

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

    The benefit of the above code is it passes the following test

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

    whilst

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

    will fail.

    0 讨论(0)
  • 2021-02-18 19:08

    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.

    0 讨论(0)
  • 2021-02-18 19:13

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-18 19:24

    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
    
    0 讨论(0)
提交回复
热议问题