Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在表达式中使用func? 背景_C#_Lambda_Delegates_Func_<img Src="//i.stack.imgur.com/WM7S8.png" Height="16" Width="18" Alt="" Class="sponsor Tag Img">ormlite Servicestack - Fatal编程技术网 ormlite-servicestack,C#,Lambda,Delegates,Func,ormlite Servicestack" /> ormlite-servicestack,C#,Lambda,Delegates,Func,ormlite Servicestack" />

C# 在表达式中使用func? 背景

C# 在表达式中使用func? 背景,c#,lambda,delegates,func,ormlite-servicestack,C#,Lambda,Delegates,Func,ormlite Servicestack,我有一个测试的例子,它通过了,但是在管道中发生了一个错误,我不知道为什么。我想弄清楚到底发生了什么,但我对表达式构造是新手,不想做任何假设 这是一个搜索过滤机制。它使用ServiceStack的PredicateBuilder实现。我基本上有一个我传入的值列表,我希望它构造一个表达式树。我以前只使用Func完成了这项工作,但我意识到我需要使用Expression。糟糕透了 目标 搜索过滤器由可重复使用的搜索过滤器类型构建而成,它由Funcs和Expressions构建而成,允许我从一个对象传入一

我有一个测试的例子,它通过了,但是在管道中发生了一个错误,我不知道为什么。我想弄清楚到底发生了什么,但我对表达式构造是新手,不想做任何假设

这是一个搜索过滤机制。它使用ServiceStack的
PredicateBuilder
实现。我基本上有一个我传入的值列表,我希望它构造一个表达式树。我以前只使用
Func
完成了这项工作,但我意识到我需要使用
Expression
。糟糕透了

目标 搜索过滤器由可重复使用的搜索过滤器类型构建而成,它由
Func
s和
Expression
s构建而成,允许我从一个对象传入一个字段名以及我应该匹配的值,最后得到可以运行
Where()
语句的内容

代码/问题 我正在尝试的通用“nullable bool”筛选器--设置可接受的项并返回一个用于帮助筛选的func:

public class NullableBoolFilter : IGenericSearchFilter<bool?>
{
    public Func<bool?, bool> GetFilterFunc(string valuesToProcess)
    {
        var acceptableValues = new List<bool?>();

        if (string.IsNullOrWhiteSpace(valuesToProcess))
        {
            // all values acceptable
            acceptableValues = new List<bool?>{true, false, null};
        }
        else
        {
            if (!valuesToProcess.Contains("0") && !valuesToProcess.Contains("1"))
            {
                throw new ArgumentException("Invalid Nullable boolean filter attribute specified");
            }
            if (valuesToProcess.Contains("0"))
            {
                acceptableValues.Add(false);

            }
            if (valuesToProcess.Contains("1"))
            {
                acceptableValues.Add(true);
            }
        }

        Func<bool?, bool> returnFunc = delegate(bool? item) { return acceptableValues.Any(x=>x == item); };
        return returnFunc;
    }
}
以下测试通过:

public class ClaimsReportIsMDLFilterTests
{
    // ReSharper disable InconsistentNaming
    private readonly vSEARCH_ClaimsReport ItemWithMDL = new vSEARCH_ClaimsReport { IsMDL = true };
    private readonly vSEARCH_ClaimsReport ItemWithoutMDL = new vSEARCH_ClaimsReport { IsMDL = false };
    private readonly vSEARCH_ClaimsReport ItemWithNullMDL = new vSEARCH_ClaimsReport { IsMDL = null };
    // ReSharper restore InconsistentNaming

    [Fact]
    public void WithSearchValueOf1_HidesNonMDLAndNull()
    {

        var sut = this.GetCompiledExpressionForValues("1");

        sut.Invoke(ItemWithMDL).Should().BeTrue();
        sut.Invoke(ItemWithoutMDL).Should().BeFalse();
        sut.Invoke(ItemWithNullMDL).Should().BeFalse();

    }

    private Func<vSEARCH_ClaimsReport, bool> GetCompiledExpressionForValues(string searchValue)
    {
        return new ClaimsReportIsMDLFilter().GetExpression(searchValue).Compile();
    }

}
公共类索赔报告DLFilterTests
{
//ReSharper禁用不一致命名
私有只读vSEARCH_ClaimsReport ItemWithMDL=新vSEARCH_ClaimsReport{IsMDL=true};
私有只读vSEARCH_ClaimsReport Itemwithout MDL=new vSEARCH_ClaimsReport{IsMDL=false};
私有只读vSEARCH_ClaimsReport ItemWithNullMDL=新vSEARCH_ClaimsReport{IsMDL=null};
//ReSharper还原不一致的命名
[事实]
搜索值为1_HidesNonMDLAndNull()的public void
{
var sut=this.GetCompiledExpressionForValues(“1”);
sut.Invoke(ItemWithMDL.Should().BeTrue();
调用(ItemWithoutMDL.Should().BeFalse();
调用(ItemWithNullMDL.Should().BeFalse();
}
private Func GetCompiledExpressionForValues(字符串搜索值)
{
返回新的ClaimsReportsMDLFILTER().GetExpression(searchValue).Compile();
}
}
问题 当我实际尝试运行此操作时,收到错误:

从作用域“”引用了类型为“vSEARCH_ClaimsReport”的变量“param”,但未定义该变量

我理解为什么会发生这种情况——在对其求值时,我没有真正的对象传递到
Func
。然而,我不明白为什么我的测试可能会通过,但在实际使用中却没有

问题
  • 为什么我的测试可能通过,但我仍然收到此错误
  • 我该怎么着手解决这个问题呢
  • 有没有一种简单的方法可以将
    Func
    转换成
    表达式
    ,我可以将字段传递到其中
  • 我是否需要放弃通用过滤器的想法,让每个类根据传入的输入手动将表达式添加到
    PredicateBuilder
    ?这是可行的,但看起来工作量可以减少很多
为什么我的考试会通过[…]

因为您的测试只是将表达式向下编译为它所表示的代码并调用它。它不需要实际解析表达式树并查看它所表示的代码在做什么,它只需要运行它并确保输出是正确的

为什么[…]我仍然会收到这个错误

因为当您实际使用它时,它不仅仅是执行代码;相反,它是通过表达式树来确定代码在做什么,以便将其转换为其他内容,而不是作为C代码运行

您的表达式只是调用一个委托。遍历表达式树的人无法看到委托内部并知道它在做什么。知道您正在调用另一种方法是无法翻译成另一种语言的

我该怎么着手解决这个问题呢

您需要从一开始就生成一个
表达式
,而不是生成一个
Func
,然后创建一个调用它的
表达式

有没有一种简单的方法可以将Func转换成一个表达式,我可以将一个字段传递给它

不需要。您需要提取函数的IL代码,将其反编译为C代码,然后构建
Expression
对象来表示该代码。这几乎不会发生


您几乎需要
GetFilterFunc
返回一个
表达式
,才能使其正常工作。幸运的是,考虑到您所拥有的,这很容易做到。您只需更改方法签名,并将最后两行替换为以下内容:

return item => acceptableValues.Any(x => x == item);
瞧。lambda可以根据上下文编译成
表达式
对象,而不是委托,因此如果方法的返回类型是
表达式
,那么您将得到它

现在,在
GetExpression
中使用它。首先,
PredicateBuilder
实际上什么都没有做。在表达式中添加一个或FALSE不会改变任何有意义的内容。所有这些都可以去。剩下的就是使用一个
表达式
,并通过拉出一个布尔属性将其更改为
表达式
。要做到这一点,表达式比代理的工作量要多一些。我们需要做更多的工作来编写它们,而不仅仅是调用表达式。我们将要编写一个方法来执行此操作:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
这样做的目的是用第一个表达式的主体替换第二个表达式参数的所有实例,有效地将该表达式内联到第二个表达式中。剩下的只是用一个新的单个参数替换所有参数,并将其包装回lambda

现在我们有了这些,我们的方法非常简单:

public Expression<Func<vSEARCH_ClaimsReport, bool>> GetExpression(
    string valuesToProcess)
{
    Expression<Func<vSEARCH_ClaimsReport, bool?>> selector = 
        item => item.IsMDL;
    return selector.Compose(base.GetFilterFunc(valuesToProcess));
}
公共表达式GetExpression(
字符串值(停止进程)
{
表达式选择器=
信息技术
public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}
public Expression<Func<vSEARCH_ClaimsReport, bool>> GetExpression(
    string valuesToProcess)
{
    Expression<Func<vSEARCH_ClaimsReport, bool?>> selector = 
        item => item.IsMDL;
    return selector.Compose(base.GetFilterFunc(valuesToProcess));
}