Dynamically casting one type of delegate to anothe

2019-04-12 13:10发布

问题:

I'm using reflection to grab a field that happens to be a delegate. I need to replace this delegate with my own, but the type of the delegate is private (so I can't create it from my method and assign it)

I have a delegate type with an exactly matching signature, so is there some way I can dynamically cast my delegate to this other type? I have a Type object representing the unknown type.

I realize what I've said above may not very clear, so here's some code:

var delegate_type = Assembly.GetAssembly(typeof(A.F))
    // public delegate in A.ZD (internal class)
    .GetType("A.ZD+WD");

The type signature of the A.ZD+WS (obfuscated name) delegate is void(System.Drawing.Graphics).

Is there a way I can cast an Action<Graphics> to this delegate type?

回答1:

This article seems to have what you want.



回答2:

It is only working for Delegates that are attached to managed methods. If trying to use Mike's article for a delegate attached to unmanaged dll function with GetDelegateForFunctionPointer, then the CreateDelegate technique will return a null attachment, and therefore crash uppon invocation. In this case I see a way to bypass the cast issue by using a wrapper class. where the abstract class has this interface:

public abstract class IInvokable
{
    public abstract T Call0<T>();
    public abstract T Call1<T, T2>(T2 arg);
    public abstract T Call2<T, T2, T3>(T2 arg1, T3 arg2);
    public abstract void SetDelegate(Delegate thedel);
    public abstract Type GetDelegateType();
}

Then the assembly you get your delegate from has to be modified to wrap the actual delegate with a class inherting from IInvokable. for example:

class Invokable : IInvokable
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int SomeDelegateTypeReturningIntTakingVoid();

    public override Type GetDelegateType()
    {
        return typeof(SomeDelegateTypeReturningIntTakingVoid);
    }

    public override void SetDelegate(Delegate thedel)
    {
        mydelegate = (SomeDelegateTypeReturningIntTakingVoid)thedel;
    }

    public SomeDelegateTypeReturningIntTakingVoidmydelegate;

    public override T Call0<T>()
    {
        return (T)(Object)mydelegate();
    }
    public override T Call1<T, T2>(T2 arg)
    {
        throw new ArgumentException("this delegate is a Call0<int>");
    }
    public override T Call2<T, T2, T3>(T2 arg1, T3 arg2)
    {
        throw new ArgumentException("this delegate has a Call0<int>");
    }
}

at this point the type must be fully "hardcoded" meaning that is can not use Func because it would prevent use of GetDelegateForFunctionPointer, because of a stupid limitation of that function (can't work with generics, because MS team is incompetent basically, c.f. msdn forums for the source on that).

my solution around this, is to use:

Type GenerateDynamicType(string sourceCode, string typenameToGet)
{
    var cp = new System.CodeDom.Compiler.CompilerParameters
    {
        GenerateInMemory = true,    // you will get a System.Reflection.Assembly back
        GenerateExecutable = false, // Dll
        IncludeDebugInformation = false,
        CompilerOptions = ""
    };

    var csharp = new Microsoft.CSharp.CSharpCodeProvider();

    // this actually runs csc.exe:
    System.CodeDom.Compiler.CompilerResults cr =
          csharp.CompileAssemblyFromSource(cp, sourceCode);


    // cr.Output contains the output from the command

    if (cr.Errors.Count != 0)
    {
        // handle errors
        throw new InvalidOperationException("error at dynamic expression compilation");
    }

    System.Reflection.Assembly a = cr.CompiledAssembly;

    // party on the type here, either via reflection...
    Type t = a.GetType(typenameToGet);
    return t;
}

as found on another answer here on StackOverflow. and generate code for the various Invokable on the fly. creating instances using:

IInvokable inv = (IInvokable)Activator.CreateInstance(GenerateDynamicType(...));

in the end a very complex system. thank you MS for being so lazy, really.