C# Delegate under the hood question
I was doing some digging aro开发者_开发技巧und into delegate variance after reading the following question in SO : Delegate.CreateDelegate() and generics: Error binding to target method
I found a very nice bit of code from Barry kelly at https://www.blogger.com/comment.g?blogID=8184237816669520763&postID=2109708553230166434
Here it is (in a sugared-up form :-)
using System;
namespace ConsoleApplication4
{
internal class Base
{
}
internal class Derived : Base
{
}
internal delegate void baseClassDelegate(Base b);
internal delegate void derivedClassDelegate(Derived d);
internal class App
{
private static void Foo1(Base b)
{
Console.WriteLine("Foo 1");
}
private static void Foo2(Derived b)
{
Console.WriteLine("Foo 2");
}
private static T CastDelegate<T>(Delegate src)
where T : class
{
return (T) (object) Delegate.CreateDelegate(
typeof (T),
src.Target,
src.Method,
true); // throw on fail
}
private static void Main()
{
baseClassDelegate a = Foo1; // works fine
derivedClassDelegate b = Foo2; // works fine
b = a.Invoke; // the easy way to assign delegate using variance, adds layer of indirection though
b(new Derived());
b = CastDelegate<derivedClassDelegate>(a); // the hard way, avoids indirection
b(new Derived());
}
}
}
I understand all of it except this one (what looks very simple) line.
b = a.Invoke; // the easy way to assign delegate using variance, adds layer of indirection though
Can anyone tell me:
- how it is possible to call invoke without passing the param required by the static function.
- When is going on under the hood when you assign the return value from calling invoke
- What does Barry mean by extra indirection (in his comment)
He isn't calling Invoke
(note the lack of ()
), he's using implicit delegate creation to set b
equal to a new derivedClassDelegate
instance that points to the Invoke
method of a
. The additional indirection is that when b
is invoked, it calls a.Invoke(new Derived())
rather than just a(new Derived())
.
To make what's actually happening more explicit:
baseClassDelegate a = Foo1; // works fine
derivedClassDelegate b = Foo2; // works fine
b = new derivedClassDelegate(a.Invoke); // the easy way to assign delegate using variance, adds layer of indirection though
b(new Derived());
b = CastDelegate<derivedClassDelegate>(a); // the hard way, avoids indirection
b(new Derived());
The first call to b
results in a chain like this (parameters eliminated for simplicity):
b() -> a.Invoke() -> Foo1()
The second call to b
results in this:
b() -> Foo1()
However
This is only needed if you need a delegate of one signature to invoke a delegate of another (less restrictive) signature. In his example, you could just set b = Foo1
and it would compile, but that wouldn't illustrate the point.
精彩评论