可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Using C# 3.5 I wanted to build up a predicate to send to a where clause piece by piece. I have created a very simple Console Application to illustrate the solution that I arrived at. This works perfectly. Absolutely perfectly. But I have NO idea how or why.
public static Func<Tran, bool> GetPredicate()
{
Func<Tran, bool> predicate = null;
predicate += t => t.Response == "00";
predicate += t => t.Amount < 100;
return predicate;
}
When I say 'predicate +=', what does that mean? predicate -= appears to do nothing and ^=, &=, *=, /= aren't liked by the compiler.
The compiler doesn't like 'predicate = predicate + t => t.Response....' either.
What have I stumbled on? I know what it does, but how does it do it?
If anyone wants to go delve into more complicated lambda's, please do so.
回答1:
"delegate += method" is operator for multicast delegate to combine method to delegate.
In the other hand "delegate -= method" is operator to remove method from delegate.
It is useful for Action.
Action action = Method1;
action += Method2;
action += Method3;
action -= Method2;
action();
In this case, only Method1 and Method3 will run, Method2 will not run because you remove the method before invoke the delegate.
If you use multicast delegate with Func, the result will be last method.
Func<int> func = () => 1;
func += () => 2;
func += () => 3;
int result = func();
In this case, the result will be 3, since method "() => 3" is the last method added to delegate. Anyway all method will be called.
In your case, method "t => t.Amount < 100" will be effective.
If you want to combine predicate, I suggest these extension methods.
public static Func<T, bool> AndAlso<T>(
this Func<T, bool> predicate1,
Func<T, bool> predicate2)
{
return arg => predicate1(arg) && predicate2(arg);
}
public static Func<T, bool> OrElse<T>(
this Func<T, bool> predicate1,
Func<T, bool> predicate2)
{
return arg => predicate1(arg) || predicate2(arg);
}
Usage
public static Func<Tran, bool> GetPredicate() {
Func<Tran, bool> predicate = null;
predicate = t => t.Response == "00";
predicate = predicate.AndAlso(t => t.Amount < 100);
return predicate;
}
EDIT: correct the name of extension methods as Keith suggest.
回答2:
When you use += or -= when delegate types, that just gives a call to Delegate.Combine
and Delegate.Remove
.
The important thing about multicast delegates is that the return value of all but the last executed delegate is ignored. They all get executed (unless an exception is thrown), but only the last return value is used.
For predicates, you might want to do something like:
public static Func<T, bool> And<T>(params Func<T, bool>[] predicates)
{
return t => predicates.All(predicate => predicate(t));
}
public static Func<T, bool> Or<T>(params Func<T, bool>[] predicates)
{
return t => predicates.Any(predicate => predicate(t));
}
You'd then do:
Func<string, bool> predicate = And<string>(
t => t.Length > 10,
t => t.Length < 20);
EDIT: Here's a more general solution which is quite fun, if a bit bizarre...
public static Func<TInput, TOuput> Combine<TInput, TOutput>
(Func<TOutput, TOutput, TOutput> aggregator,
params Func<TInput, TOuput>[] delegates) {
// delegates[0] provides the initial value
return t => delegates.Skip(1).Aggregate(delegates[0](t), aggregator);
}
So you could then implement And as:
public static Func<T, bool> And<T>(params Func<T, bool>[] predicates) {
return Combine<T, bool>((x, y) => x && y, predicates);
}
(I personally prefer this over using GetInvocationList()
, because you end up with a predicate you can pass to other bits of LINQ etc.)
回答3:
Actually, that doesn't work. Try to test it with a case where the first condition FAILS but the second one passes. You'll find that it'll return true. The reason is, because when dealing with multicast delegates that return values, only the last value is returned. For example:
Func<string, bool> predicate = null;
predicate += t => t.Length > 10;
predicate += t => t.Length < 20;
bool b = predicate("12345");
This will return TRUE because the last function call returns true (it's less than 20). In order to really make it work, you need to call:
predicate.GetInvocationList();
which returns an array of delegates. You then need to make sure they ALL return true, for the final result to be true. Make sense?
回答4:
Expanding on BFree's answer (+1'd it)
If you want to get the behavior you're looking for, you'll need to explicitly chain the predicates together. Here is an example
public static Func<Tran, bool> GetPredicate()
{
Func<Tran, bool> predicate1 = t => t.Response == "00";
Func<Tran, bool> predicate2 = t => t.Amount < 100;
return t => predicate1(t) && predicate2(t);
}
回答5:
If you want to combine predicates, try this;
public static Predicate<T> Combine<T>(params Predicate<T>[] predicates)
{
return t => predicates.All(pr => pr(t));
}
You call it like this;
Predicate<string> shortAndSweet = Combine<string>
(
s => s.Length < 10, // short,
s => s == "sweet" // and sweet
);
回答6:
The += is syntactic sugar specifically implemented to support adding handlers to events. Since events are just a special case of delegate, and Func is also a delegate, the syntax appears to work here.
But are you sure it works as expected? By that I mean, do you expect an AND or OR evaluation? How would you implement the opposite if you wanted it? Are you sure it's not just returning the result of the first? Or the last?