在C#中连接Lambda函数
使用C#3.5我想构建一个谓词,逐个发送到where子句。 我创建了一个非常简单的控制台应用程序来说明我到达的解决方案。 这非常有效。 绝对完美。 但我不知道如何或为什么。
public static Func GetPredicate() { Func predicate = null; predicate += t => t.Response == "00"; predicate += t => t.Amount < 100; return predicate; }
当我说’谓词+ =’时,这是什么意思? 谓词 – =似乎什么都不做,编译器不喜欢^ =,&=,* =,/ =。
编译器不喜欢’predicate = predicate + t => t.Response ….’。
我偶然发现了什么? 我知道它的作用,但它是如何做到的?
如果有人想深入研究更复杂的lambda,请这样做。
“delegate + = method”是多播委托的运算符,用于将方法组合到委托。 另一方面,“委托 – =方法”是从委托中删除方法的运算符。 它对Action非常有用。
Action action = Method1; action += Method2; action += Method3; action -= Method2; action();
在这种情况下,只运行Method1和Method3,因为在调用委托之前删除了方法,所以不会运行Method2。
如果您使用带有Func的多播委托,则结果将是最后一个方法。
Func func = () => 1; func += () => 2; func += () => 3; int result = func();
在这种情况下,结果将是3,因为方法“()=> 3”是添加到委托的最后一个方法。 无论如何,所有方法都将被调用。
在您的情况下,方法“t => t.Amount <100”将是有效的。
如果你想组合谓词,我建议这些扩展方法。
public static Func AndAlso( this Func predicate1, Func predicate2) { return arg => predicate1(arg) && predicate2(arg); } public static Func OrElse ( this Func predicate1, Func predicate2) { return arg => predicate1(arg) || predicate2(arg); }
用法
public static Func GetPredicate() { Func predicate = null; predicate = t => t.Response == "00"; predicate = predicate.AndAlso(t => t.Amount < 100); return predicate; }
编辑:更正Keith建议的扩展方法的名称。
当委托类型使用+ =或 – =时,只调用Delegate.Combine
和Delegate.Remove
。
关于多播委托的重要一点是忽略了除最后执行的委托之外的所有委托的返回值。 它们都被执行(除非抛出exception),但只使用了最后一个返回值。
对于谓词,您可能希望执行以下操作:
public static Func And(params Func[] predicates) { return t => predicates.All(predicate => predicate(t)); } public static Func Or (params Func[] predicates) { return t => predicates.Any(predicate => predicate(t)); }
然后你会这样做:
Func predicate = And( t => t.Length > 10, t => t.Length < 20);
编辑:这是一个更通用的解决方案,非常有趣,如果有点奇怪......
public static Func Combine (Func aggregator, params Func[] delegates) { // delegates[0] provides the initial value return t => delegates.Skip(1).Aggregate(delegates[0](t), aggregator); }
所以你可以实现And as:
public static Func And(params Func[] predicates) { return Combine((x, y) => x && y, predicates); }
(我个人更喜欢使用GetInvocationList()
,因为你最终会得到一个谓词,你可以传递给LINQ的其他位等。)
实际上,这不起作用。 尝试用第一个条件FAILS但第二个条件通过的情况进行测试。 你会发现它会回归真实。 原因是,因为在处理返回值的多播委托时,只返回最后一个值。 例如:
Func predicate = null; predicate += t => t.Length > 10; predicate += t => t.Length < 20; bool b = predicate("12345");
这将返回TRUE,因为最后一个函数调用返回true(它小于20)。 为了真正使它工作,你需要打电话:
predicate.GetInvocationList();
它返回一个委托数组。 然后,您需要确保它们全部返回true,以使最终结果为真。 合理?
扩展了BFree的答案(+ 1)
如果你想获得你正在寻找的行为,你需要明确地将谓词链接在一起。 这是一个例子
public static Func GetPredicate() { Func predicate1 = t => t.Response == "00"; Func predicate2 = t => t.Amount < 100; return t => predicate1(t) && predicate2(t); }
如果你想组合谓词,试试这个;
public static Predicate Combine (params Predicate [] predicates) { return t => predicates.All(pr => pr(t)); }
你这样称呼它;
Predicate shortAndSweet = Combine ( s => s.Length < 10, // short, s => s == "sweet" // and sweet );
+ =是专门用于支持向事件添加处理程序的语法糖。 由于事件只是委托的特例,而Func也是委托,因此语法似乎在此处起作用。
但你确定它按预期工作吗? 我的意思是,你期望进行AND或OR评估吗? 如果你想要的话,你会如何实现相反的目标呢? 你确定它不只是返回第一个结果吗? 还是最后一次?