从函数表达式主体中提取强类型表达式

问题描述 投票:0回答:1

我正在尝试编写一种“宽松”的比较方法,该方法对表达式进行操作并在实体框架中使用。它由一系列

Replace()
调用和
ToLower()
组成,用于对复合名称执行不精确匹配。

例如,“OConnor”将在此轻松搜索中匹配“o'connor”,“jean luc”将匹配“Jean-Luc”。等等

我编写了一个辅助方法,它为传入的每个值链接一个

Replace()
调用,以从输入字符串中删除该值。此方法有效,但我无法弄清楚在将其与属性访问表达式和常量字符串一起使用时如何保持强类型。

这里是一个强类型变体,接受返回字符串的函数表达式:

private Expression ExpressionStrip<T>(Expression<Func<T, string>> exprStr, params string[] saValues)
{
    if (saValues.Length == 0)
    {
        return exprStr.Body;
    }

    var exprEmptyString = Expression.Constant("");
    var exprResult = Expression.Call(exprStr.Body, "Replace", null, Expression.Constant(saValues[0]), exprEmptyString);

    for (var i = 1; i < saValues.Length; i++)
    {
        exprResult = Expression.Call(exprResult, "Replace", null, Expression.Constant(saValues[i]), exprEmptyString);
    }

    return exprResult;
}

使用示例:

var stripped = ExpressionStrip(p => p.FullName.LastName, " ", "-", "'", "’");

但是我还需要对常量执行相同的操作来进行比较:

var stripped2 = ExpressionStrip("Jean-Luc", " ", "-", "'", "’");

我已经使用基本

Expression
类型完成了此操作:

private Expression ExpressionStrip(Expression str, params string[] saValues)
{
    if (saValues.Length == 0)
    {
        return str;
    }

    var exprEmptyString = Expression.Constant("");
    var exprResult = Expression.Call(str, "Replace", null, Expression.Constant(saValues[0]), exprEmptyString);

    for (var i = 1; i < saValues.Length; i++)
    {
        exprResult = Expression.Call(exprResult, "Replace", null, Expression.Constant(saValues[i]), exprEmptyString);
    }

    return exprResult;
}       

我可以通过传入 lambda 表达式的表达式主体来使用它:

// Where exprStr is of type Expression<Func<T, string>>
ExpressionStrip(exprStr.Body, " ", "-", "'", "’");

并且还通过传递常量:

ExpressionStrip(Expression.Constant("Jean-Luc"), " ", "-", "'", "’");

所以,它有效,但我失去了

Expression
的强类型,它解析为
String
,所以这种东西编译:
ExpressionStrip(Expression.Constant(true), " ")
,我不希望它这样做。

我尝试了这个签名:

Expression ExpressionStrip(Expression<string> str, params string[] saValues)
但是
LambdaExpression.Body
返回
Expression
,它无法转换为
Expression<string>
,所以我不能将它与我的
exprStr.Body
一起使用。

是否可以以某种方式从 lambda 函数表达式中转换/转换/提取强类型函数体?

c# linq expression linq-expressions
1个回答
0
投票

根据设计,表达式树都是弱类型的,除了 lambda 表达式。类型检查大部分都是在运行时完成的。

Expression<T>
表示一个 lambda 表达式,其类型是委托类型
T
。一般来说,它并不意味着“
T
类型的表达式”。没有任何类型可以代表这一点。

您能得到的最接近的可能就是采取

Expression<Func<string>>

private Expression ExpressionStrip(Expression<Func<string>> exprStr, params string[] saValues) { ... }

然后,要将

Expression<Func<T, string>>
传递给此,您可以:

// suppose exprStr is of type Expression<Func<T, string>>
ExpressionStrip(Expression.Lambda<Func<string>>(exprStr.Body), "x", "y", "z");

有人可能会说这仍然不是类型安全的,因为你可以用任何东西替换

exprStr.Body

// this compiles and throws an exception at runtime
ExpressionStrip(Expression.Lambda<Func<string>>(Expression.Constnat(1)), "x", "y", "z");

这又是因为表达式树的设计是弱类型的。

© www.soinside.com 2019 - 2024. All rights reserved.