从表达式<func>列表中获取Func类型

时间:2016-07-11 12:22:22

标签: c#

我在课程上有以下列表:

public class Mapper {

  public List<Expression> Expressions { get; set; } = new List<Expression>();

}

我知道所有表达式都是:

Expression<Func<InType, OutType>>

问题是InType和OutType在列表中有所不同......

我尝试定义类似但不编译的东西。

Expression<Func<,>>

稍后我将需要浏览每个Expression并获取函数的输入和输出类型。

这可能吗?

7 个答案:

答案 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具有ReturnTypeParameters属性。 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));