c#.NET 2.0中有什么办法!结合多个谓词?
假设我有以下代码。
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;
}
}
这给了我:
Emma
Ethan
Emily
所以这很酷,但我知道希望能够使用多个谓词进行过滤。
所以我希望能够说出这样的话:
List<string> filteredNames = names.FindAll(StartsWithE OR StartsWithI);
为了得到:
Emma
Isabella
Ethan
Emily
我怎样才能做到这一点? 目前我只是过滤完整列表两次,然后将结果合并。但不幸的是,这是非常无效的,更重要的是我失去了原始的排序顺序,这在我的情况下是不可接受的。
我还需要能够迭代任意数量的过滤器/谓词,因为可能会有很多。
再一次,它需要是一个.NET 2.0解决方案,遗憾的是我无法使用更新版本的框架
非常感谢。
答案 0 :(得分:55)
怎么样:
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;
};
}
为了完整性:
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;
};
}
然后用:
调用它List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE, StartsWithI));
另一种选择是使用多播委托,然后使用GetInvocationList()
拆分它们,然后执行相同的操作。然后你可以这样做:
List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE+StartsWithI));
我不是后一种方法的忠实粉丝 - 感觉有点滥用多播。
答案 1 :(得分:29)
我想你可以这样写:
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));
......等等。
答案 2 :(得分:5)
我最近提出了类似于这个问题的解决方案,这也可能有所帮助。我为列表扩展了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;
}
}
它返回一个列表,其中只包含与所有给定谓词匹配的项目。当然,它可以很容易地改为OR所有谓词而不是AND。但仅凭这一点,就可以组合出各种各样的逻辑组合。
用法:
{
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
}
答案 3 :(得分:1)
在.NET 2.0中,您可以使用匿名委托:
List<string> filteredNames = names.FindAll(
delegate(string s) { return StartsWithE(s) OR StartsWithI(s); }
);
事实上,您也可以使用它来替换您的功能:
List<string> filteredNames = names.FindAll(
delegate(string s) { return s.StartsWith("E") || s.StartsWith("I"); }
);
答案 4 :(得分:0)
您可以创建第三个谓词,在内部对结果进行OR运算。我想你可以使用lambda表达式动态执行此操作。像这样的东西(这不是lambda表达式,因为我对snytax不太好):
static bool StartsWithEorI(string s)
{
return StartsWithE(s) || StartsWithI(s);
}
答案 5 :(得分:0)
您可以将谓词方法包装到一个类中,让构造函数接受要测试的字符串数组:
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;
}
}
然后你可以这样打电话:
List<string> filtered = names.FindAll((new StartsWithPredicate("E", "I")).StartsWith);
通过这种方式,您可以测试输入字符串的任意组合,而无需使用StartsWith
方法的新变体扩展代码库。
答案 6 :(得分:0)
在上述'params'数组方法中广泛使用了这种模式后,我最近了解了多播委托,这让我很感兴趣。由于委托固有地支持列表(或多播),因此您可以跳过params []模式,并为Test()函数提供单个委托。您需要在提供的Predicate&lt;&gt;上调用GetInvokationList。请参阅:Multicast delegate of type Func (with return value)?