0

I need to create delegates with Reflection out of methods such as the following:

public void Serialize<TElement>(IList<TElement> value)
{
}

These methods have in common that they return Void and have one parameter with one generic argument. The parameter type doesn't specifically have to be IList<T>. I can't resolve TElement until before the delegate's invocation.

Using the following method raises an exception:

static Delegate CreateOpenDelegate(MethodInfo method, object target)
{
    var args = new List<Type>(method.GetParameters()
        .Select(param => param.ParameterType));
    args.Add(method.ReturnType);
    var delegateType = Expression.GetDelegateType(args.ToArray());
    return Delegate.CreateDelegate(delegateType, target, method);
}

Is there a viable alternative to creating the delegate in runtime, just before its execution (once TElement becomes known and MakeGenericMethod can be called on the method)?

Bombinosh
  • 413
  • 6
  • 18
e_ne
  • 8,340
  • 32
  • 43
  • Have you seen this ? http://stackoverflow.com/a/308040/361899 – aybe Oct 10 '14 at 20:37
  • 1
    You can't have an object of an open type. How exactly would you like to use this? – svick Oct 10 '14 at 22:26
  • @svick A serializer will call that method when an object of type IList is passed to it, regardless of what T is. Methods are mapped to the types they operate on at startup and are called through a serializer compiled in runtime with expression trees. – e_ne Oct 11 '14 at 01:24

1 Answers1

0

My understanding is that the Delegate class (not to be confused with the delegate keyword) can only be created from a MethodInfo that could be Invoked. A MethodInfo cannot be invoked if IsGenericMethod is true and ContainsGenericParameters = true. More Information on this.

That being said, if all the types are known at runtime when you need to create the delegate, it is possible to construct a Delegate from them. If for some reason they aren't, you may need to restructure your code so that you create the delegate more closely to its invocation.

static Delegate CreateOpenDelegate(MethodInfo method, object instance, params Type[] genericParameters)
{
    var myMethod = method.IsGenericMethod ? method.MakeGenericMethod(genericParameters) : method;
    var args = new List<Type>(myMethod.GetParameters()
        .Select(param => param.ParameterType));
    args.Add(myMethod.ReturnType);
    var delegateType = Expression.GetDelegateType(args.ToArray());
    return myMethod.CreateDelegate(delegateType, instance);
}

Here is an example using Serialize, but as a static instance on the class Program (just because it was convenient, you can do it as an instance parameter if you prefer).

static void Main(string[] args)
{
    var method = typeof(Program).GetMethod("Serialize");
    object myCollection = "12345".ToCharArray();
    //Get the element type for the array. In this case it is typeof(char)
    Delegate test = CreateOpenDelegate(method, null, myCollection.GetType().GetElementType());
    test.DynamicInvoke(myCollection);

    myCollection = new List<int> { 1, 2, 3, 4, 5 };
    //Get the generic argument for the collection type. In this case it is typeof(int)
    test = CreateOpenDelegate(method, null, myCollection.GetType().GetGenericArguments()[0]);
    test.DynamicInvoke(myCollection);
}

public static void Serialize<TElement>(IList<TElement> value)
{
}

The type of collection may vary depending on your circumstances. I got the type in two different manners, but this too can be abstracted better by calling GetInterfaces() on GetType() and then finding the IList interface and getting the generic type from that interface.

The biggest mistake you may make when first using this is mistakenly passing in the entire IList type as the generic parameter instead of getting the IList generic parameter. At least I know that I still do it frequently.

Brandon Barkley
  • 720
  • 6
  • 21