C# 组合多个谓词
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1248232/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Combine Multiple Predicates
提问by eric
Is there any way in c# .NET 2.0! to combine multiple Predicates?
在 c# .NET 2.0 中有什么方法吗!组合多个谓词?
Let's say I have the following code.
假设我有以下代码。
List<string> names = new List<string>();
names.Add("Jacob");
names.Add("Emma");
names.Add("Michael");
names.Add("Isabella");
names.Add("Ethan");
names.Add("Emily");
List<string> filteredNames = names.FindAll(StartsWithE);
static bool StartsWithE(string s)
{
if (s.StartsWith("E"))
{
return true;
}
else
{
return false;
}
}
This gives me:
这给了我:
Emma
Ethan
Emily
So this is pretty cool stuff, but I know want to be able to filter using multiple predicates.
所以这是很酷的东西,但我知道希望能够使用多个谓词进行过滤。
So I want to be able to say something like this:
所以我希望能够说这样的话:
List<string> filteredNames = names.FindAll(StartsWithE OR StartsWithI);
In order to get:
为了得到:
Emma
Isabella
Ethan
Emily
How can I achieve this? Currently I am just filtering the complete list twice and combining the results afterwards. But unfortunately this is quite inefficent and even more importantly I lose the original sort order, which is not acceptable in my situation.
我怎样才能做到这一点?目前我只是过滤了两次完整的列表,然后再结合结果。但不幸的是,这非常低效,更重要的是我丢失了原始排序顺序,这在我的情况下是不可接受的。
I also need to be able to iterate over any number of filters/predicates as there can be quite a lot.
我还需要能够迭代任意数量的过滤器/谓词,因为可能有很多。
Again it needs to be a .NET 2.0 solution unfortunately I can't use a newer version of the framework
同样,它需要是 .NET 2.0 解决方案,不幸的是我不能使用较新版本的框架
Thanks a lot.
非常感谢。
采纳答案by Jon Skeet
How about:
怎么样:
public static Predicate<T> Or<T>(params Predicate<T>[] predicates)
{
return delegate (T item)
{
foreach (Predicate<T> predicate in predicates)
{
if (predicate(item))
{
return true;
}
}
return false;
};
}
And for completeness:
为了完整性:
public static Predicate<T> And<T>(params Predicate<T>[] predicates)
{
return delegate (T item)
{
foreach (Predicate<T> predicate in predicates)
{
if (!predicate(item))
{
return false;
}
}
return true;
};
}
Then call it with:
然后调用它:
List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE, StartsWithI));
Another alternative would be to use multicast delegates and then split them using GetInvocationList()
, then do the same thing. Then you could do:
另一种选择是使用多播委托,然后使用 拆分它们GetInvocationList()
,然后做同样的事情。那么你可以这样做:
List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE+StartsWithI));
I'm not a huge fan of the latter approach though - it feels like a bit of an abuse of multicasting.
不过,我不是后一种方法的忠实拥护者 - 感觉有点滥用多播。
回答by AaronLS
You could create a third predicate that internally ORs the results together. I think you could do this on the fly using a lambda expression. Something like this(this is not a lambda expression as I'm not too good with that snytax):
您可以创建第三个谓词,在内部对结果进行 OR 运算。我认为您可以使用 lambda 表达式即时执行此操作。像这样的东西(这不是一个 lambda 表达式,因为我不太擅长那个 snytax):
static bool StartsWithEorI(string s)
{
return StartsWithE(s) || StartsWithI(s);
}
回答by Fredrik M?rk
You could wrap the predicate method into a class and have the constructor accept an array of strings to test for:
您可以将谓词方法包装到一个类中,并让构造函数接受要测试的字符串数组:
class StartsWithPredicate
{
private string[] _startStrings;
public StartsWithPredicate(params string[] startStrings)
{
_startStrings = startStrings;
}
public bool StartsWith(string s)
{
foreach (var test in _startStrings)
{
if (s.StartsWith(test))
{
return true;
}
}
return false;
}
}
Then you can make a call like this:
然后你可以像这样拨打电话:
List<string> filtered = names.FindAll((new StartsWithPredicate("E", "I")).StartsWith);
That way you can test for any combination of input strings without needing to extend the code base with new variations of the StartsWith
method.
这样您就可以测试输入字符串的任意组合,而无需使用该StartsWith
方法的新变体来扩展代码库。
回答by Pavel Minaev
In .NET 2.0, there are anonymous delegates which you can use there:
在 .NET 2.0 中,您可以使用匿名委托:
List<string> filteredNames = names.FindAll(
delegate(string s) { return StartsWithE(s) OR StartsWithI(s); }
);
In fact, you can use it to replace your functions as well:
事实上,你也可以用它来替换你的函数:
List<string> filteredNames = names.FindAll(
delegate(string s) { return s.StartsWith("E") || s.StartsWith("I"); }
);
回答by Andrey Ilnitsky
I guess you could write something like this:
我想你可以这样写:
Func<string, bool> predicate1 = s => s.StartsWith("E");
Func<string, bool> predicate2 = s => s.StartsWith("I");
Func<string, bool> combinedOr = s => (predicate1(s) || predicate2(s));
Func<string, bool> combinedAnd = s => (predicate1(s) && predicate2(s));
... and so on.
... 等等。
回答by Battle
I just recently came up with a solution similar to this problem, which could be also helpful. I expanded the FindAll method for lists, allowing me to stack predicates in lists as I needed:
我最近才想出了一个类似于这个问题的解决方案,这也可能会有所帮助。我扩展了列表的 FindAll 方法,允许我根据需要在列表中堆叠谓词:
public static class ExtensionMethods
{
public static List<T> FindAll<T> (this List<T> list, List<Predicate<T>> predicates)
{
List<T> L = new List<T> ();
foreach (T item in list)
{
bool pass = true;
foreach (Predicate<T> p in predicates)
{
if (!(p (item)))
{
pass = false;
break;
}
}
if (pass) L.Add (item);
}
return L;
}
}
It returns a list with only the items matching all given predicates. Of course it can easily be altered to OR all predicates instead of AND. But with that alone one can assemble quite a good variety of logical combinations.
它返回一个列表,其中仅包含与所有给定谓词匹配的项目。当然,它可以很容易地更改为 OR 所有谓词而不是 AND。但仅凭这一点,就可以组合出相当多的逻辑组合。
Usage:
用法:
{
List<Predicate<int>> P = new List<Predicate<int>> ();
P.Add (j => j > 100);
P.Add (j => j % 5 == 0 || j % 7 == 0);
P.Add (j => j < 1000);
List<int> L = new List<int> () { 0, 1, 2, ... 999, 1000 }
List<int> result = L.FindAll (P);
// result will contain: 105, 110, 112, 115, 119, 120, ... 994, 995
}
回答by Brian Broussard
Having used this pattern extensively with the above 'params' array method, I was intrigued by recently learning about the Multicast delegate. Since delegates inherently support a list (or multicast), you can skip the params[] pattern and just supply a single delegate to your Test() function. You will need to call GetInvokationList on the supplied Predicate<>. See this: Multicast delegate of type Func (with return value)?
在将这种模式广泛用于上述 'params' 数组方法之后,我对最近了解多播委托很感兴趣。由于委托本身支持列表(或多播),因此您可以跳过 params[] 模式,只为您的 Test() 函数提供一个委托。您需要对提供的 Predicate<> 调用 GetInvokationList。请参阅:Func 类型的多播委托(带返回值)?