从表达式<函数>列表中获取 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<,>>

稍后,我将需要遍历每个表达式并获取函数的输入和输出类型。

这可能吗?

从表达式<函数>列表中获取 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 具有 ReturnTypeParameters 属性。 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));