设置任何 Func<> 的参数
Set parameter of any Func<>
我有一个 func<>、Func<,>、func<,> 中任何一种类型的对象,我想用常量值替换其中一个输入参数。
例如:
object SetParameter<T>(object function, int index, T value){
//I don't know how to code this.
}
Func<int, String, String> function = (a, b) => a.ToString() + b;
object objectFunction = function;
object newFunction = SetParameter<int>(objectFunction, 0, 5);
// Here the new function should be a Func<String, String> which value "(b) => function(5, b)"
我现在已经知道如何获取结果函数的类型,但这并不能真正帮助我实现所需的行为:
private Type GetNewFunctionType<T>(object originalFunction, int index, T value)
{
Type genericType = originalFunction.GetType();
if (genericType.IsGenericType)
{
var types = genericType.GetGenericArguments().ToList();
types.RemoveAt(index);
Type genericTypeDefinition = genericType.GetGenericTypeDefinition();
return genericTypeDefinition.MakeGenericType(types.ToArray());
}
throw new InvalidOperationException($"{nameof(originalFunction)} must be a generic type");
}
不太清楚你转换的目的是什么,但是避免所有反射不是更容易吗?例如:
Func<int, string, string> func3 = (a, b) => a.ToString() + b;
Func<string, string> func3withConst = (b) => func3(10, b);
由于您谈论的范围非常有限(仅支持 Func<TReturn>
、Func<T1, TReturn>
和 Func<T1, T2, TReturn>
),因此通过反射执行此操作更容易出错且更难阅读。
以防万一你需要 use expression tree to build the function:
object SetParameter<T>(object function, int index, T value)
{
var parameterTypes = function.GetType().GetGenericArguments();
// Skip where i == index
var newFuncParameterTypes = parameterTypes.SkipWhile((_, i) => i == index).ToArray();
// Let's assume function is Fun<,,> to make this example simple :)
var newFuncType = typeof(Func<,>).MakeGenericType(newFuncParameterTypes);
// Now build a new function using expression tree.
var methodCallParameterTypes = parameterTypes.Reverse().Skip(1).Reverse().ToArray();
var methodCallParameters = methodCallParameterTypes.Select(
(t, i) => i == index
? (Expression)Expression.Constant(value, typeof(T))
: Expression.Parameter(t, "b")
).ToArray();
// func.Invoke(5, b)
var callFunction = Expression.Invoke(
Expression.Constant(function),
methodCallParameters);
// b => func.Invoke(5, b)
var newFunc = Expression.Lambda(
newFuncType,
callFunction,
methodCallParameters.OfType<ParameterExpression>()
).Compile();
return newFunc;
}
要使用这个:
Func<int, string, string> func = (a, b) => a.ToString() + b;
var newFunc = (Func<string, string>)SetParameter<int>(func, 0, 5);
// Output: 5b
Console.WriteLine(newFunc("b"));
我有一个 func<>、Func<,>、func<,> 中任何一种类型的对象,我想用常量值替换其中一个输入参数。
例如:
object SetParameter<T>(object function, int index, T value){
//I don't know how to code this.
}
Func<int, String, String> function = (a, b) => a.ToString() + b;
object objectFunction = function;
object newFunction = SetParameter<int>(objectFunction, 0, 5);
// Here the new function should be a Func<String, String> which value "(b) => function(5, b)"
我现在已经知道如何获取结果函数的类型,但这并不能真正帮助我实现所需的行为:
private Type GetNewFunctionType<T>(object originalFunction, int index, T value)
{
Type genericType = originalFunction.GetType();
if (genericType.IsGenericType)
{
var types = genericType.GetGenericArguments().ToList();
types.RemoveAt(index);
Type genericTypeDefinition = genericType.GetGenericTypeDefinition();
return genericTypeDefinition.MakeGenericType(types.ToArray());
}
throw new InvalidOperationException($"{nameof(originalFunction)} must be a generic type");
}
不太清楚你转换的目的是什么,但是避免所有反射不是更容易吗?例如:
Func<int, string, string> func3 = (a, b) => a.ToString() + b;
Func<string, string> func3withConst = (b) => func3(10, b);
由于您谈论的范围非常有限(仅支持 Func<TReturn>
、Func<T1, TReturn>
和 Func<T1, T2, TReturn>
),因此通过反射执行此操作更容易出错且更难阅读。
以防万一你需要 use expression tree to build the function:
object SetParameter<T>(object function, int index, T value)
{
var parameterTypes = function.GetType().GetGenericArguments();
// Skip where i == index
var newFuncParameterTypes = parameterTypes.SkipWhile((_, i) => i == index).ToArray();
// Let's assume function is Fun<,,> to make this example simple :)
var newFuncType = typeof(Func<,>).MakeGenericType(newFuncParameterTypes);
// Now build a new function using expression tree.
var methodCallParameterTypes = parameterTypes.Reverse().Skip(1).Reverse().ToArray();
var methodCallParameters = methodCallParameterTypes.Select(
(t, i) => i == index
? (Expression)Expression.Constant(value, typeof(T))
: Expression.Parameter(t, "b")
).ToArray();
// func.Invoke(5, b)
var callFunction = Expression.Invoke(
Expression.Constant(function),
methodCallParameters);
// b => func.Invoke(5, b)
var newFunc = Expression.Lambda(
newFuncType,
callFunction,
methodCallParameters.OfType<ParameterExpression>()
).Compile();
return newFunc;
}
要使用这个:
Func<int, string, string> func = (a, b) => a.ToString() + b;
var newFunc = (Func<string, string>)SetParameter<int>(func, 0, 5);
// Output: 5b
Console.WriteLine(newFunc("b"));