从表达式<函数>列表中获取 Func 类型
本文关键字:获取 Func 类型 列表 表达式 函数 | 更新日期: 2023-09-27 17:56:33
我有一个类的以下列表:
public class Mapper {
public List<Expression> Expressions { get; set; } = new List<Expression>();
}
我知道所有表达式都是:
Expression<Func<InType, OutType>>
问题是列表中的输入类型和输出类型不同...
我试图定义类似的东西,但它无法编译。
Expression<Func<,>>
稍后,我将需要遍历每个表达式并获取函数的输入和输出类型。
这可能吗?
不,这通常是不可能的。
像Expression<Func<_,_>>
这样的类型称为高级类型。C# 语言无法表达此类类型。
以下代码
List<Expression> list = new List<Expression>();
var typePairs = list.OfType<LambdaExpression>().Select(x =>
new
{
inType = x.Parameters[0].Type,
outType = x.ReturnType
});
我希望你愿意做一些反思。仅使用编译时类型是不可能的,但通过反射,您可以在运行时提取类型。
缺点是:
- 必须确保列表仅包含
Expression<Func<In, Out>>
,而不包含其他类型的表达式。(无法在编译时检查) - 它很慢
示例代码:
class Program
{
public static void ConsumeExpressions(List<LambdaExpression> exprs)
{
var consumerMethod = typeof(Program).GetMethod("ConsumeExpression", BindingFlags.Public | BindingFlags.Static);
foreach (var expr in exprs)
{
var inType = expr.Parameters[0].Type;
var outType = expr.ReturnType;
var genericMethod = consumerMethod.MakeGenericMethod(inType, outType);
genericMethod.Invoke(null, new object[] { expr });
}
}
public static void ConsumeExpression<TInType, TOutType>(Expression<Func<TInType, TOutType>> expr)
{
Console.WriteLine("in: {0}, out: {1}, {2}", typeof(TInType).Name, typeof(TOutType).Name, expr);
}
static void Main(string[] args)
{
ConsumeExpressions(new List<LambdaExpression>
{
(Expression<Func<int, string>>)(i => ""),
(Expression<Func<string, int>>)(s => 0)
});
}
}
编辑
建议使用基于Brahim帖子的LambdaExpression
s,这样反射部分就会变得更短更清晰。
这假设只有一个参数:
public List<LambdaExpression> Expressions { get; set; } = new List<LambdaExpression>();
foreach (LambdaExpression expression in Expressions)
{
var inType = expression.Paramters[0].Type;
var outType = expresssion.ReturnType;
}
根据用例,最好使 in 和 out 类型具有公共基类或共享接口,这样就不需要检查它们的类型。
编辑:
只有 LambdaExpressions 具有 ReturnType
和 Parameters
属性。 Expression<TDelegate>
继承自LambdaExpression
.
所有类型都派生自对象...您可以轻松地使用对象,但会失去所需的类型安全性。
也许定义一个接口IParameter来定义逻辑的语义,例如InType,OutType,HasInputValue,HasOutputValue。
使用接口实现来解决问题。
也许定义一个 Number 或 NumberResult 类来实现该接口,并将其或接口用作列表中公开的类型。
这里有一个可以帮助您的完整示例
class Program
{
static void Main(string[] args)
{
List<Expression> expressions = new List<Expression>();
Expression <Func<string,int>> func = a => a.Length;
expressions.Add(func);
foreach (var expression in expressions)
{
LambdaExpression lmdExpression = expression as LambdaExpression;
if (lmdExpression!=null)
{
//get all params
List<string> paramsList = new List<string>();
foreach (var parameterExpression in lmdExpression.Parameters)
{ paramsList.Add(parameterExpression.Type.ToString());
}
var returnedType = lmdExpression.ReturnType.ToString();
//and here you can use a big switch to invoke your needed expression
}
}
Console.ReadLine();
}
}
为什么使用 Expression 和 LambdaExpression?
因为LambdaExpression
是一个抽象类,从中派生Expression<TDelegate>
并且由于您不知道正在使用哪种类型的Func<T,T>
,因此必须使用基类
更新:感谢所有同事和他们的评论。替换了我原来的答案。
首先,你绝对不需要Expression
来完成这项工作,因为最重要的是,它是一个扭曲的类型定位器。看看下面的实现:
public class FuncMapper
{
Dictionary<Type, Delegate> _funcs = new Dictionary<Type, Delegate>();
public void Register<TIn, TOut>(Func<TIn, TOut> func)
{
_funcs.Add(func.GetType(), func);
}
public TOut Execute<TIn, TOut>(TIn param)
{
return ((Func<TIn, TOut>)_funcs[typeof(Func<TIn, TOut>)])(param);
}
}
那么使用它很简单,就像这样:
class Program
{
static void Main(string[] args)
{
FuncMapper funcMapper = new FuncMapper();
funcMapper.Register<string, string>(DoString);
funcMapper.Register<int, int>(DoInt);
Console.WriteLine("Value: {0}", funcMapper.Execute<string, string>("Test"));
Console.WriteLine("Value: {0}", funcMapper.Execute<int, int>(10));
Console.Read();
}
static string DoString(string param)
{
return param;
}
static int DoInt(int param)
{
return param;
}
}
更新2:如果出于某种原因确实需要表达式,可以通过以下方式更改Register
方法:
public void Register<TIn, TOut>(Expression<Func<TIn, TOut>> expression)
{
Func<TIn, TOut> func = expression.Compile();
_funcs.Add(func.GetType(), func);
}
然后这样称呼它:
funcMapper.Register<string, string>((param) => DoString(param));
funcMapper.Register<int, int>((param) => DoInt(param));