왜 Predicate <T> 대신 Func <T, bool>입니까?


210

이것은 누군가 호기심이 많은 질문입니다.

.NET Framework 클래스 라이브러리에는 다음 두 가지 방법이 있습니다.

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, bool>> predicate
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

Func<TSource, bool>대신에 사용 Predicate<TSource>합니까? 등이 보인다는 Predicate<TSource>전용으로 사용 List<T>하고 Array<T>있는 동안, Func<TSource, bool>거의 모든에 의해 사용되는 QueryableEnumerable방법과 확장 방법이 무엇이야 ...?


21
네, 일관성없는 사용법은 저를 미치게합니다.
George Mauer

답변:


170

반면 Predicate같은 시간에 도입 된 것으로 List<T>하고 Array<T>, .NET 2.0, 다른 FuncAction변형 .NET 3.5에서 왔습니다.

따라서 이러한 Func술어는 주로 LINQ 연산자의 일관성을 위해 사용됩니다. 사용에 대한 .NET 3.5 이후는, Func<T>지침 상태 :Action<T>

새로운 LINQ 유형을 사용하십니까 Func<>하고 Expression<>대신 사용자 대표와 술어의


7
시원, = 전에 그 길드 라인을 본 적이 없다 =)
Svish

6
대부분의 투표권이 있으므로 답변으로 수락합니다. Jon Skeet은 많은 담당자를 보유하고 있기 때문에 ... : p
Svish

4
그것은 쓰여진 기괴한 지침입니다. "새 LINQ 유형"Func <> "및"Action <> "[...]"을 사용해야합니다. Expression <>은 완전히 다른 것입니다.
Jon Skeet

3
실제로, 당신은 LINQ 공급자를 작성하는 것에 관한 지침의 맥락에서 그것을 배치해야합니다. 따라서 LINQ 연산자의 경우 확장 방법의 매개 변수로 Func <> 및 Expression <>을 사용하는 지침이 있습니다. Func와 Action 사용에 관한 별도의 지침에 감사하겠습니다
Jb Evain

Skeet의 요점은 Expression <>이 지침이 아니라는 것입니다. 해당 유형을 인식하고 다른 유형을 수행하는 것은 C # 컴파일러 기능입니다.
Daniel Earwicker

116

나는 이것을 전에 궁금했다. 나는 Predicate<T>대의원을 좋아한다 -그것은 훌륭하고 묘사 적이다. 그러나 다음의 과부하를 고려해야합니다 Where.

Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

항목의 색인을 기준으로 필터링 할 수도 있습니다. 훌륭하고 일관된 반면,

Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

그렇지 않을 것입니다.


11
술어 <int, bool>는 다소 추악합니다. 술어는 일반적으로 (컴퓨터 과학의 IME) 단일 값으로 술어됩니다. 그것은 물론 Predicate <Pair <T, int >> 일 수 있지만, 그것은 더 나쁘다 :)
Jon Skeet

너무 사실이야 ... 아니요, Func이 더 깨끗하다고 ​​생각합니다.
Svish

2
지침으로 인해 다른 답변을 수락했습니다. 그래도 하나 이상의 답변을 표시 할 수 있기를 바랍니다! 많은 답변을 "매우 주목할만한"또는 "또한 답변 외에 추가로 지적해야 할 좋은 점이 있음"으로 표시 할 수 있다면 좋을 것입니다.
Svish

6
흠, 방금 내가 첫 번째 주석을 잘못 쓴 것을 보았습니다 : P 내 제안은 물론 Predicate <T, int>가 될 것입니다 ...
Svish

4
@ JonSkeet 나는이 질문이 오래되었다는 것을 알고 있지만 아이러니 한 것이 무엇인지 알고 있습니까? Where확장 메소드 의 매개 변수 이름은 입니다 predicate. 허 = P.
콘래드 클라크

32

확실히 Func특정 대리자 대신 사용하는 실제 이유 는 C #이 별도로 선언 된 대리자를 완전히 다른 유형으로 취급하기 때문입니다.

비록 Func<int, bool>Predicate<int>모두 동일한 인수 및 반환 유형이, 그들은 할당 호환되지 않습니다. 따라서 모든 라이브러리가 각 델리게이트 패턴에 대해 고유 한 델리게이트 유형을 선언 한 경우 사용자가 "브리지"델리게이트를 삽입하여 변환을 수행하지 않으면 해당 라이브러리는 상호 운용 할 수 없습니다.

    // declare two delegate types, completely identical but different names:
    public delegate void ExceptionHandler1(Exception x);
    public delegate void ExceptionHandler2(Exception x);

    // a method that is compatible with either of them:
    public static void MyExceptionHandler(Exception x)
    {
        Console.WriteLine(x.Message);
    }

    static void Main(string[] args)
    {
        // can assign any method having the right pattern
        ExceptionHandler1 x1 = MyExceptionHandler; 

        // and yet cannot assign a delegate with identical declaration!
        ExceptionHandler2 x2 = x1; // error at compile time
    }

모든 사용자가 Func를 사용하도록 장려함으로써 Microsoft는 이것이 호환되지 않는 대리자 유형의 문제를 완화하기를 희망합니다. 모든 대의원은 매개 변수 / 반환 유형에 따라 일치하기 때문에 잘 어울립니다.

Func(및 Action)이 out또는 ref매개 변수를 가질 수 없기 때문에 모든 문제를 해결하지는 못하지만 덜 일반적으로 사용됩니다.

업데이트 : 의견 Svish는 말합니다 :

그럼에도 불구하고 매개 변수 유형을 Func에서 Predicate로 또는 그 반대로 전환해도 아무런 차이가없는 것 같습니다. 적어도 문제없이 컴파일됩니다.

예, 프로그램이 내 Main함수 의 첫 번째 줄에서와 같이 대리자에게만 메서드를 할당하는 한 . 컴파일러는 메소드로 전달하는 델리게이트 오브젝트를 새로 작성하기 위해 자동으로 코드를 생성합니다. 그래서 내 Main기능에서 문제를 일으키지 않고 x1유형으로 변경할 수 있습니다 ExceptionHandler2.

그러나 두 번째 줄에서 첫 번째 대리인을 다른 대리인에게 할당하려고합니다. 두 번째 델리게이트 유형이 정확히 동일한 매개 변수 및 반환 유형을 가지고 있다고 생각하더라도 컴파일러는 error를 제공합니다 CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'.

아마도 이것이 더 명확해질 것입니다 :

public static bool IsNegative(int x)
{
    return x < 0;
}

static void Main(string[] args)
{
    Predicate<int> p = IsNegative;
    Func<int, bool> f = IsNegative;

    p = f; // Not allowed
}

내 방법 IsNegative은 직접 수행 하는 한 pf변수 에 할당하는 것이 가장 좋습니다 . 그러나 그런 변수 중 하나를 다른 변수에 할당 할 수 없습니다.


여전히 매개 변수 유형을 Func <T, bool>에서 Predicate <T>로 전환했다가 다시 변경해도 차이가없는 것 같습니까? 최소한 문제없이 컴파일됩니다.
Svish

MS는 개발자들이 이것들을 동일하게 생각하지 못하게하려고 노력하는 것 같습니다. 왜 궁금해?
Matt Kocaj

1
전달되는 표현식이 메소드 호출에 별도로 정의 된 경우 매개 변수 유형을 전환하면 차이가 발생합니다. 그 이유는 컴파일러에서 유형을 유추하지 않고 Func<T, bool>또는 유형으로 입력 Predicate<T>되기 때문입니다.
Adam Ralph

30

(3.5 이상) 조언은 사용하는 것입니다 Action<...>Func<...>- "왜?"를위한 - " Predicate<T>"는 "predicate"의 의미를 아는 경우에만 " "가 의미가 있다는 것입니다. 그렇지 않으면 signatute를 찾기 위해 객체 브라우저 등을 살펴 봐야합니다.

반대로 Func<T,bool>표준 패턴을 따릅니다. 나는 이것이 a를 취하고 a T를 반환하는 함수라는 것을 즉시 알 수 있다bool -어떤 용어를 이해할 필요가 없습니다-단지 진리 테스트를 적용하십시오.

"predicate"의 경우 이것은 괜찮 았지만 표준화하려는 시도에 감사합니다. 또한 해당 영역의 관련 방법과 많은 패리티를 허용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.