2013-09-25 93 views
3

对于一些奇特的反射材料,我有一个类型为Func的函数,需要将它传递给一个接受类型Func的函数,其中T在运行时间之前是未知的。例如:创建一种函数将一种类型的函数转换为另一种类型

public bool MyOperation(Func<string,bool> op) { 
    return _myValues.Any(op); 
} 

public static bool InvokeOperationMethod(MethodInfo info, object obj,Func<object,bool> opAsObject) 
{ 
    info.Invoke(obj, opAsObject); 
} 

的问题是,因为我有一个弱类型的拉姆达,我不能把它作为一个更强的类型的参数。所以我试图建立一个帮助器,它将创建一个将弱类型的lambda转换为更强类型的函数。例如,我可以打电话给

var converter = CreateConverter(typeof(string)); 
Func<object,bool> asObject = o => o.ToString() == "a string"; //Dump example 
Func<string,bool> asString = (Func<string,bool>)converter(asObject); 
Assert.IsTrue(asInt("a string")); 

当然在实际的代码此目标类型不知道,直到运行时间,以及实际的谓词不是一些琐碎的考验。

这是我的尝试:

/// <summary> 
/// Converts a predicate of Func<object,bool> to 
/// Func<Type,bool> of the given type. 
/// </summary> 
/// <param name="destType">Type of the dest.</param> 
/// <param name="predicate">The predicate.</param> 
/// <returns></returns> 
public static TransformPredicate CreateConverter(Type destType) 
{ 
    // This essentially creates the following lambda, but uses destType instead of T 
    // private static Func<Func<object, bool>, Func<T, bool>> Transform<T>() 
    // { 
    //  return (Func<object,bool> input) => ((T x) => input(x)); 
    // } 
    var input = Expression.Parameter(typeof(Func<object, bool>), "input"); 

    var x = Expression.Parameter(destType, "x"); 
    var convert = Expression.Convert(x, typeof(object)); 
    var callInputOnX = Expression.Invoke(input, convert); 
    var body2 = Expression.Lambda(callInputOnX, x); 
    var body1 = Expression.Lambda(typeof(TransformPredicate),body2, input); 
    return (TransformPredicate) body1.Compile(); 
} 

public delegate object TransformPredicate(Func<object,bool> weak); 

其实,这只是正常工作,但它运行非常缓慢,因为它隐含在每次调用调用createDelegate方法。所以,我想通过增加调用createDelegate方法自己:

var destFunc = typeof(Func<,>).MakeGenericType(destType, typeof(bool)); 
var endType = typeof(Func<,>).MakeGenericType(typeof(Func<object, bool>), destFunc); 
return (TransformPredicate)compiled.Method.CreateDelegate(endType); 

这将导致一个错误:

System.NotSupportedException: Derived classes must provide and implementation.

任何想法如何,我可以叫我createDelegate方法?

+0

这可能有助于http://msdn.microsoft.com/en-us/library/bb292051.aspx –

+0

我不明白。为什么你不能只调用asObject(“字符串”)?我认为重要的是你将如何使用这个结果,所以如果你表明这一点会有所帮助。 – svick

回答

1

实际上,只要目标类型是引用类型,您就不必做任何事情。 Func<T, TResult>中的类型参数T逆变函数,这意味着您可以直接进行强制转换。正因为如此,下面的代码工作正常:

Func<object,bool> asObject = o => o.ToString() == "a string"; 
Func<string,bool> asString = (Func<string,bool>)asObject; 
asString("a string"); 

编辑:这不是编译器做转换,它是理解Func<object, bool>可以安全地铸造到Func<string, bool>的CLR。所以,下面的代码会工作得很好:

class Program 
{ 
    static void Main() 
    { 
     InvokeOperationMethod(
      typeof(Program).GetMethod("MyOperation"), 
      new Program(), o => o.ToString() == "42"); 
    } 

    public bool MyOperation(Func<string, bool> op) 
    { 
     return op("43"); 
    } 

    public static bool InvokeOperationMethod(
     MethodInfo info, object obj, Func<object, bool> opAsObject) 
    { 
     return (bool)info.Invoke(obj, new object[] { opAsObject }); 
    } 
} 

编辑2:如果你需要这个值类型的工作也一样,你就需要以某种方式框中的参数。拳击本身很简单:

private static Func<T, bool> BoxParameter<T>(Func<object, bool> op) 
{ 
    return x => op(x); 
} 

但你需要调用它,因为你不知道T在编译的时候,你需要使用反射了点。喜欢的东西:

public static bool InvokeOperationMethod(
    MethodInfo method, object obj, Func<object, bool> opAsObject) 
{ 
    var targetType = method.GetParameters() 
          .Single() 
          .ParameterType 
          .GetGenericArguments()[0]; 

    object opAsT; 
    if (targetType.IsValueType) 
    { 
     opAsT = 
      typeof(Program).GetMethod("BoxParameter", 
      BindingFlags.NonPublic | BindingFlags.Static) 
          .MakeGenericMethod(targetType) 
          .Invoke(null, new object[] {opAsObject}); 
    } 
    else 
    { 
     opAsT = opAsObject; 
    } 

    return (bool)method.Invoke(obj, new[] { opAsT }); 
} 
+0

我试图澄清我需要在反射中发生什么,以及为什么我需要实际制作一个转换器而不是依靠编译器来为我执行转换。 – heneryville

+0

@heneryville不,你不需要,看编辑。 – svick

+0

你说得对。我忘了我需要的原因是有时目标类型不是由参考。任何想法如何做到这一点?我一直对object和ref类型之间的关系感到困惑,因为我可以这样做:object o = 1; o.Dump(); – heneryville

相关问题