2015-02-05 3 views
0

У меня есть метод, возвращающий Func<ConstructorInfo, MyDelegate<T>>, где MyDelegate является общим (ковариантным) делегатом. Теперь я хочу присвоить результат этого метода переменной типа Func<ConstructorInfo, MyDelegate<U>>, где U является базовым классом от T (T: U). Но я получаю InvalidCastException. Это потому, что Func не является ковариантным (по крайней мере, в .NET 3.5) или как я могу это исправить?не может отличить Func <T1, T2<U>> to Func <T1, T2<V>> с V: U

EDIT некоторого код:

using System; 
using System.Reflection; 
using System.Linq.Expressions; 

public delegate T MyDelegate<out T>(params object[] args); 

class GenericTypeFactory 
{ 
    public static MyDelegate<T> GetActivator<T>(ConstructorInfo ctor) 
    { 
      // make a NewExpression that calls the ctor with the args we 
      // just created 
      var newExp = Expression.New(ctor); 
      var lambda = Expression.Lambda(typeof(MyDelegate<T>), newExp); 
      var compiledExpression = (MyDelegate<T>)lambda.Compile(); 
      return compiledExpression; 
    } 
} 

И в моем основном классе:

using System; 
using System.Reflection; 
using System.Linq.Expressions; 

class MyClass { 
    private Func<ConstructorInfo, MyDelegate<T>> createLambdaExpression<T>() 
    { 
     // this Func is only introduced to ensure name-safety when 
     // refactoring the GenericTypeFactor.GetActivator-method 
     // (changing params, other name, ...) we could also use 
     // Reflection to get this method by its name the generic-type 
     // argument <T> is only a placeholder 
     Func<ConstructorInfo, Delegate> myFunc = 
      GenericTypeFactory.GetActivator<T>; 

     MethodInfo method = myFunc.Method; 
     // set the params for the method we obtained above 
     var paramExp = Expression.Parameter(typeof(ConstructorInfo), "ctor"); 
     // call the method with its params 
     var call = Expression.Call(method, paramExp);     
     return Expression.Lambda<Func<ConstructorInfo, MyDelegate<T>>>(
       call, 
       paramExp) 
      .Compile(); 
    } 

    void DoSomeThing<T>() 
    { 
     Type customType = typeof(T); // not really, but for simplicity 
     // type T is only a dummy-type (replaced in next line) 
     Func<Delegate> myFunc = this.createLambdaExpression<T>; 
     MethodInfo method = 
      myFunc.Method.GetGenericMethodDefinition() 
      .MakeGenericMethod(customType); 
     var getActivator = (Func<ConstructorInfo, MyDelegate<T>>) 
      method.Invoke(this, null); 
    } 
} 
+5

Вы используете * .NET 3.5? Это действительно поможет, если вы дадите более подробную информацию о своей среде и короткую, но полную программу, демонстрирующую проблему. –

+0

Yeap, я использую 3.5. Подождите немного, мне нужно построить короткую тестовую программу ... – HimBromBeere

+0

@JonSkeet Это не имеет ничего общего с версией .NET Framework. –

ответ

0

I'm до сих пор не знает, почему он не работает так, как я написал выше. Теперь я нашел другой способ, который по крайней мере работает (хотя его не мой любимый). Для этого я просто передаю результат моего метода createLambdaExpression для делегата вместо конкретного Func и вызываю его DynamicInvoke-метод. Это, конечно, не является оптимальным с точки зрения производительности, но поскольку я кэширую все мои активаторы, мне просто нужно вызвать его один раз, а не всегда, мне нужен новый экземпляр определенного типа.

Func<Delegate> myFunc = this.createLambdaExpression<T>; 
MethodInfo method = myFunc.Method.GetGenericMethodDefinition().MakeGenericMethod(customType); 
var getActivator = (Delegate)method.Invoke(this, null); 
var activator = (Delegate)getActivator.DynamicInvoke(new[] { ctor }); 

Наконец я называю ((ObjectActivator<T>) activator)() который будет возвращать новый экземпляр типа customType.