2009-08-08 4 views
24

C# .NET 2.0에는 방법이 있습니까? 여러 술어를 결합하려면?여러 술어 결합

다음과 같은 코드가 있다고 가정 해 보겠습니다.

Emma 
Ethan 
Emily 

그래서이 꽤 멋진 물건,하지만 난 여러 조건을 사용하여 필터링 할 수 있도록하려는 알고

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; 
    } 
} 

이 날 수 있습니다. 위해

List<string> filteredNames = names.FindAll(StartsWithE OR StartsWithI); 

이 얻을 :

Emma 
Isabella 
Ethan 
Emily 

가 어떻게 이것을 달성 할 수

그래서 나는이 같은 말을 할 수있게하려면? 현재 전체 목록을 두 번 필터링하고 그 결과를 나중에 조합합니다. 하지만 불행히도 이것은 상당히 비효율적이며 더욱 중요한 것은 원래의 정렬 순서를 잃어 버렸기 때문에 내 상황에서는 받아 들일 수 없습니다.

또한 많은 수의 필터/조건자를 반복 할 수 있어야합니다.

다시는 불행하게도 내가 프레임 워크의

덕분에 많은 최신 버전을 사용할 수있는 .NET 2.0 솔루션을해야합니다.

답변

46

방법에 대해 멀티 캐스트 위임을 사용하는 것입니다

List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE, StartsWithI)); 

또 다른 한 후 사용하여 분할 :

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; 
    }; 
} 

를 다음에 호출 GetInvocationList()을 입력 한 다음 동일한 작업을 수행하십시오. 그럼 당신은 할 수 :

List<string> filteredNames = names.FindAll(Helpers.Or(StartsWithE+StartsWithI)); 

그래도 난 후자의 접근 방식의 거대한 팬이 아니다 - 그것은 멀티 캐스팅의 남용 약간의 느낌.

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"); } 
); 
+0

고마워! 완벽하게 작동합니다! – eric

+0

매우 편리합니다. ICollectionView에서 필터에 대한 여러 술어가 필요합니다. – pStan

+0

전체 함수는'return item => predicates.All (술어 => 술어 (항목));' – NibblyPig

0

결과를 내부적으로 OR하는 세 번째 조건부를 만들 수 있습니다. 나는 당신이 람다 식 (lambda expression)을 사용하여 파리에서 이것을 할 수 있다고 생각합니다. 이런 식으로 뭔가 (내가 그 snytax 너무 좋은 아니에요으로 이것은 람다 식 아닙니다) :

static bool StartsWithEorI(string s) 
{ 
    return StartsWithE(s) || StartsWithI(s); 
} 
+0

물론이 예제 에서처럼 많은 조합이 있습니다. 심지어 세 가지 필터를 결합에 대해 생각하지 ... 또한 다시 불행히도 나는 단지 .NET 2.0을 사용할 수 있습니다 – eric

+0

아, 그건 불행합니다. 익명의 대리자 나 람다 식을 사용했다면 원하는 힘을 결합하여 "즉석에서"제공 할 수있을 것입니다. – AaronLS

0

당신은 테스트 할 문자열 배열을 수락 클래스로 술어 방법을 포장하고 생성자를 가질 수있다 :

List<string> filtered = names.FindAll((new StartsWithPredicate("E", "I")).StartsWith); 

새로운 변화와 코드베이스를 확장 할 필요없이 입력 문자열의 조합을 테스트 할 수 있습니다 그 방법 :

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; 
    } 
} 

그런 다음이 같은 전화를 걸 수 있습니다 방법은 StartsWith입니다.

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; 
    }; 
} 

그리고 완성도 :

1

, 당신이 사용할 수있는 익명의 대의원이있다 다음과 같이 쓸 수 있습니다 :

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)); 

... 등등.

22

을 나는 당신을 생각

.NET 2.0
+0

좋은 해결책 :) –

2

저는 최근에이 문제와 유사한 해결책을 찾았습니다. 도움이 될 수도 있습니다.

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) 
     { 
      foreach (Predicate<T> p in predicates) 
      { 
       if (!(p (item))) break; 
      } 
      L.Add (item); 
     } 
     return L; 
    } 
} 

그것은 모든 주어진 조건에 일치하는 항목 만있는 목록을 반환 : 나는 필요에 따라 나를 목록에 조건을 스택 할 수 있도록 목록의 findall은 방법을 확대했다. 물론 AND 대신에 모든 술어를 쉽게 OR로 변경할 수 있습니다. 그러나 그것만으로도 꽤 다양한 논리 조합을 조합 할 수 있습니다.

사용법 :

{ 
    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 
} 

그것은 더 이상 날 데려 갔어 난이 함께 와서 인정하고 싶습니다. 그러나 대표단 대표자 ...

0

db에 LINQ를 사용하는 경우 훌륭한 솔루션 here을 발견했습니다. 완벽하게 작동합니다. .NET 2.0에서 작동하는지 여부도 확실하지 않습니다.

0

위의 'params'배열 방법으로이 패턴을 광범위하게 사용했기 때문에 저는 최근에 멀티 캐스트 대리자에 대해 배웠습니다. 델리게이트가 기본적으로리스트 (또는 멀티 캐스트)를 지원하기 때문에 params [] 패턴을 건너 뛰고 Test() 함수에 하나의 델리게이트 만 제공하면됩니다. 제공된 조건부 <>에서 GetInvokationList를 호출해야합니다. 이것을보십시오 : Multicast delegate of type Func (with return value)?