对于一些花哨的反射内容,我有一个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,我无法将其作为更强类型的参数传递。所以我尝试创建一个帮助程序,它将创建一个将较弱类型的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。所以我尝试通过添加:
来自己调用CreateDelegatevar 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:派生类必须提供和实现。
我可以自己调用CreateDelegate的任何想法吗?
答案 0 :(得分:1)
实际上,只要目标类型是引用类型,您就不必执行任何操作。 T
中的类型参数Func<T, TResult>
是逆变,这意味着您可以直接执行转换。因此,以下代码可以正常工作:
Func<object,bool> asObject = o => o.ToString() == "a string";
Func<string,bool> asString = (Func<string,bool>)asObject;
asString("a string");
编辑:编译器不是进行转换,而是CLR理解Func<object, bool>
可以安全地投放到Func<string, bool>
。因此,以下代码可以正常工作:
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 });
}