我在课程上有以下列表:
public class Mapper {
public List<Expression> Expressions { get; set; } = new List<Expression>();
}
我知道所有表达式都是:
Expression<Func<InType, OutType>>
问题是InType和OutType在列表中有所不同......
我尝试定义类似但不编译的东西。
Expression<Func<,>>
稍后我将需要浏览每个Expression并获取函数的输入和输出类型。
这可能吗?
答案 0 :(得分:1)
这假设只有一个参数:
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
。
答案 1 :(得分:1)
不,这通常不可能。
类似Expression<Func<_,_>>
的类型称为更高级别的类型。 C#语言无法表达这些类型。
答案 2 :(得分:1)
您可以使用以下代码
List<Expression> list = new List<Expression>();
var typePairs = list.OfType<LambdaExpression>().Select(x =>
new
{
inType = x.Parameters[0].Type,
outType = x.ReturnType
});
答案 3 :(得分:1)
我希望你愿意做一些反思。仅使用编译时类型是不可能的,但使用反射可以在运行时提取类型。
缺点是:
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
,因此反射部分变得更短更清晰。
答案 4 :(得分:0)
所有类型都来自对象......你很容易使用对象,但会失去你想要的类型安全性。
也许定义一个接口IParameter,它定义了你的逻辑的语义,例如: InType,OutType,HasInputValue,HasOutputValue。
使用界面实现来解决问题。
可能定义一个Number或NumberResult类,它实现该接口并使用它或接口作为列表中公开的类型。
答案 5 :(得分:0)
这里有一个可以帮助你的完整示例
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>
是哪种类型,所以你必须使用基类
答案 6 :(得分:-2)
更新:感谢所有同事和他们的评论。取代了原来的答案。
首先,你绝对不需要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;
}
}
Update2:如果您确实需要表达式,无论出于何种原因,您都可以通过以下方式更改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));