对于一些花哨的反射东西,我有一个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。所以我尝试通过添加以下内容来自己调用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:派生类必须提供和实现。
有什么想法我可以自己叫CreateDelegate吗?
实际上,只要目标类型是引用类型,您就无需执行任何操作。该类型参数T
的Func<T, TResult>
是逆变,这意味着你可以直接做演员。因此,以下代码可以正常工作:
Func<object,bool> asObject = o => o.ToString() == "a string";
Func<string,bool> asString = (Func<string,bool>)asObject;
asString("a string");
编辑:不是由编译器进行转换,而是Func<object, bool>
可以安全地转换为的CLR 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 });
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句