Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a delegate with unknown parameters and return type

Tags:

c#

delegates

I'm trying to create a method which returns a delegate of a given type, with a body running certain code specified in the function. i.e.

    public Delegate GenerateDelegate(Type delegateType)
    {
        // create a delegate of type 'Type'
        // specify the code of this delegate
        // return the delegate object
    }

If I knew the type, or at least parameters & return-type beforehand, I could obviously just return a delegate of that type, an anonymous one, with whatever code I want. However, the problem is that I want this to work for pretty much any type of non-generic delegate and I want to be able to make use of the parameters the delegate type has, and return the correct type.

I've taken a look at different options of using ILGenerator.Emit() or CodeDOM to achieve this. However I may want to do quite complex things within the returned delegate, and would prefer to be able to code this directly in C# as I'm not sure I can achieve this using IL or CodeDOM. As a pseudo-solution, in the same fashion as Delegate.DynamicInvoke(params object[]) can take anonymous parameters, I'm thinking I'd like to be able to do something like this.

    public Delegate GenerateDelegate(Type delegateType)
    {
        return Delegate.CreateDelegate(delegateType, GetType().GetMethod("RunEvent"));
    }

    public static object RunEvent(params object[] parameters)
    {
        // some specified code, depending on contents of parameters
        return null;
    }

The RunEvent() can take any number of parameters as an object array, and returns object. Sadly, this does not work and throws the following exception:

Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

Somehow I think the object array parameters should be compatible with any combination of parameters a delegate may have, but the return type being an object, might return some object not compatible with the given delegateType, or if the delegateType returns void. This may cause some problems.

For my own usage, I know all the parameters and return-type at run-time, and can check for this to make sure everything is safe. Is there any way I can get something like this working?

like image 510
jsmars Avatar asked Oct 31 '22 09:10

jsmars


1 Answers

I found a solution for my problem, it does however require me to use generics. In a way its a safer solution, as I'll need to supply the generator with the requested delegate and its types.

While the solution isn't quite what I was hoping for, it does solve the problem for me at the moment and answer my original question. I may however have to move to a more complex solution in the future.

If anyone can see a further simplification of this, please let me know.

(I discovered this thanks to Anis suggestion on exploring Expression Trees, as it takes a similar approach.)

public class DelegateGenerator<TDelegate, TReturn> 
    : GenBase<TDelegate, TReturn> where TDelegate : class
{
    public TReturn RunEvent() => runEvent();
}
public class DelegateGenerator<TDelegate, TReturn, T> 
    : GenBase<TDelegate, TReturn> where TDelegate : class
{
    public TReturn RunEvent(T a) => runEvent(a);
}
public class DelegateGenerator<TDelegate, TReturn, T1, T2> 
    : GenBase<TDelegate, TReturn> where TDelegate : class
{
    public TReturn RunEvent(T1 a, T2 b) => runEvent(a, b);
}
// etc

public abstract class GenBase<TDelegate, TReturn> where TDelegate : class
{
    public TDelegate GenerateDelegate()
    {
        var method = GetType().GetMethod("RunEvent");
        return Delegate.CreateDelegate(typeof(TDelegate), this, method) as TDelegate;
    }

    protected TReturn runEvent(params object[] objs)
    {
        // some code
        return (TReturn)result;
    }
}

And usage:

public delegate bool myDelegate(int a, bool b);

var gen = new DelegateGenerator<myDelegate, bool, int, bool>();
var del = gen.GenerateDelegate();

var result = del(5, true);
like image 135
jsmars Avatar answered Nov 15 '22 05:11

jsmars