问题
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?
回答1:
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
回答2:
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);
}
}
}
回答3:
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.
回答4:
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.
回答5:
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.
来源:https://stackoverflow.com/questions/4040475/how-to-convert-delegate-to-identical-delegate