LINQ : 전혀 그렇지 않다


272

제공된 값이 목록의 값과 일치하는지 확인하려는 경우가 있습니다 (예 : 유효성 검사시).

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

최근 ReSharper가 다음과 같은 쿼리를 단순화하도록 요청했습니다.

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

분명히 이것은 논리적으로 동일하며 약간 더 읽기 쉽습니다 (많은 수학을 한 경우), 내 질문은 : 이것이 성능 저하를 초래합니까?

그것은 마치 느낌이 들지만 (즉 .Any(), 단락되는 .All()것처럼 들리지만 소리는 들리지 않습니다), 나는 이것을 입증 할 것이 없습니다. 쿼리를 통해 동일한 문제를 해결할지 또는 ReSharper가 나를 타락시키는 지에 대해 더 깊이 알고 있습니까?


6
Linq 코드를 해체하여 어떤 작업을했는지 보셨습니까?
— RQDQ

9
이 경우에 나는 실제로 if (! acceptedValues.Contains (someValue))와 함께 갈 것이지만 물론 이것은 문제가되지 않았다 :)
— csgero

2
@csgero 동의합니다. 위의 실제 논리 단순화 (아마도 지나치게 단순화).
— Mark

1
"그렇게 느껴지는 느낌이다 (예 : .Any ()는 단락 된 것처럼 들리지만 .All ()은 그렇지 않은 것처럼 들린다)"-직관력이없는 사람에게는 해당되지 않습니다. 당신이 주목하는 논리적 인 동등성은 그것들이 똑같이 단락 가능하다는 것을 의미합니다. 잠시 생각하면 비 적격 사례가 발생하자마자 모두가 종료 될 수 있습니다.
— Jim Balter

3
나는 이것에 대해 ReSharper에 보편적으로 동의하지 않습니다. 현명한 생각의 기차를 쓰십시오. 필요한 항목이없는 경우 예외를 발생 시키려면 : if (!sequence.Any(v => v == true)). 모든 것이 특정 사양을 준수하는 경우에만 계속하려면 : if (sequence.All(v => v < 10)).
— Timo

답변:


344

AllILSpy 에 따른 구현 (실제로 "잘, 그 방법은 약간 비슷하게 작동하는 것"보다는 실제로 갔다가 보았 기 때문에 영향보다는 이론을 논의하는 경우에 할 수있다).

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (!predicate(current))
        {
            return false;
        }
    }
    return true;
}

AnyILSpy 에 따라 구현 :

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    foreach (TSource current in source)
    {
        if (predicate(current))
        {
            return true;
        }
    }
    return false;
}

물론, 생성 된 IL에는 약간의 미묘한 차이가있을 수 있습니다. 그러나 아닙니다. IL은 거의 동일하지만 술어 일치시 true를 리턴하는 것과 술어 불일치시 false를 리턴하는 것의 명백한 역전을 나타냅니다.

이것은 물론 linq-for-objects입니다. 다른 linq 제공자는 다른 linq 제공자가 다른 것보다 하나를 훨씬 잘 처리 할 수 ​​있지만,이 경우 더 최적의 구현을 가진 것은 무작위입니다.

이 규칙 if(determineSomethingTrue)은보다 단순하고 읽기 쉬운 사람에게만 적용되는 것 같습니다 if(!determineSomethingFalse). 그리고 공정성에서, 나는 그들이 if(!someTest)우리가 행동하고자하는 조건에 대해 진실로 되돌아 갈 수있는 동등한 상세 성과 복잡성에 대한 대안적인 테스트가있을 때 종종 혼란스러워 한다는 점을 생각 합니다. 그러나 실제로, 나는 개인적으로 당신이 제공하는 두 가지 대안 중 하나를 선호하는 것을 찾지 못하며, 술어가 더 복잡하다면 아마도 전자에게 약간 기울어 질 것입니다.

* 나는 이해하지 못하는 것처럼 혼란스럽지 않지만, 나는 이해하지 못하는 결정에 미묘한 이유가 있다고 걱정하고, "아니, 그들은 단지 그런 식으로 기다렸다가 다시이 코드를보고 있던 것이 무엇입니까? ... "


8
나는 줄 뒤에서 무엇을했는지 확신하지 못하지만 if (아무것도 아닌) if (모두 같지 않음)보다 훨씬 더 읽기 쉽습니다.
— VikciaR

49
열거 형에 값이 없으면 큰 차이가 있습니다. 'Any'는 항상 FALSE를 반환하고 'All'은 항상 TRUE를 반환합니다. 따라서 하나보다 다른 것보다 논리적으로 같다고 말하는 것은 완전히 사실이 아닙니다!
— Arnaud

44
@Arnaud Any가 돌아 false오고 따라서 !Any돌아올 true것이므로 동일합니다.
— 존 한나

11
@Arnaud 어떤 사람과 모든 사람이 논리적으로 동등하다고 말한 사람은 없습니다. 또는 달리 말하면, 모든 사람들은 Any와 All이 논리적으로 동등하다고 말하지 않았습니다. 동등성은! Any (predicate)와 All (! predicate) 사이입니다.
— Jim Balter

7
@MacsDickinson 그것은 반대의 술어를 비교하지 않기 때문에 전혀 차이가 없습니다. 에 상응 !test.Any(x => x.Key == 3 && x.Value == 1)하는 용도 All이다 test.All(x => !(x.Key == 3 && x.Value == 1))(사실상 동등하다 test.All(x => x.Key != 3 || x.Value != 1)).
— 존 한나

55

이러한 확장 방법을 사용하면 코드를 더 읽기 쉽게 만들 수 있습니다.

public static bool None<TSource>(this IEnumerable<TSource> source)
{
    return !source.Any();
}

public static bool None<TSource>(this IEnumerable<TSource> source, 
                                 Func<TSource, bool> predicate)
{
    return !source.Any(predicate);
}

이제 원래 대신

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

넌 말할 수있다

if (acceptedValues.None(v => v == someValue))
{
    // exception logic
}

6
고맙습니다-이미 커먼즈 라이브러리에서 구현하려고 생각했지만 아직 좋은 아이디어인지 결정하지 못했습니다. 나는 그들이 코드를 더 읽기 쉽게 만든다는 데 동의하지만, 충분한 가치를 추가하지 못한다고 우려합니다.
— Mark

2
나는 없음을 찾아 찾지 못했습니다. 훨씬 더 읽기 쉽습니다.
— Rhyous

null 검사를 추가해야했습니다 : return source == null || ! source.Any (predicate);
— Rhyous

27

- 둘 다 결과 후 모두 정지 열거 판단 할 수 있기 때문에 동일한 성능 것이다 Any()첫 번째 항목에 전달 된 술어 평가합니다로 true와 All()의 술어 평가하여 첫 번째 항목에를 false.


21

All 첫 번째 비 일치에 단락이 발생하므로 문제가되지 않습니다.

미묘의 한 영역은

 bool allEven = Enumerable.Empty<int>().All(i => i % 2 == 0); 

사실이다. 시퀀스의 모든 항목이 균일합니다.

이 방법에 대한 자세한 내용은 Enumerable.All 설명서를 참조하십시오 .


12
예,하지만 bool allEven = !Enumerable.Empty<int>().Any(i => i % 2 != 0)마찬가지입니다.
— Jon Hanna

1
@Jon 의미 적으로 none! = all. 따라서 의미 적으로 .All ()의 경우 아무것도 없거나 모두 가지고 있지만 none은 all에 대해 true를 반환하는 모든 컬렉션의 하위 집합 일 뿐이며 불일치하면 불일치로 인해 버그가 발생할 수 있습니다. Anthony
— Rune FS에

@RuneFS 팔로우하지 않습니다. 의미 상 논리적으로 "아무것도 아닌 곳 ..."은 "그 곳의 모든 곳"과 동일합니다. 예를 들어 "우리 회사에서 승인 된 프로젝트가없는 곳은?" "다른 회사의 모든 허용 된 프로젝트는 어디입니까?"와 같은 대답을 항상 갖습니다.
— Jon Hanna

... "모든 항목이 ..."라고 가정하여 버그를 가질 수 있다는 것은 사실입니다. "모든 항목은 ..."이므로 테스트를 수행하는 하나 이상의 항목이 하나 이상 있다는 것을 의미합니다. "빈 세트에 대해서는 항상 사실입니다. 전혀 논쟁의 여지가 없습니다. "None of items ..."를 가정 할 때 동일한 문제가 발생할 수 있다고 덧붙였지만, "none of items ..."도 빈 세트에 대해 항상 참이므로 하나 이상의 항목이 테스트를 수행하지 않음을 의미합니다. . 안토니의 견해에 동의하지 않는 것은 아닙니다. 논의중인 두 구성 중 다른 구성 요소에도 적용된다고 생각합니다.
— Jon Hanna

@Jon 당신은 논리를 말하고 있고 언어학을 이야기하고 있습니다. 인간의 두뇌는 부정적인 것을 처리 할 수 ​​없으며 (그 시점에서 긍정적으로 처리하기 전에 그것을 부정 할 수 있습니다) 그런 의미에서 둘 사이에는 상당한 차이가 있습니다. 그것은 당신이 제안한 논리를 부정확하게 만들지 않습니다
— Rune FS

8

All()시퀀스의 모든 요소가 조건을 만족하는지 여부를 결정합니다.
Any()시퀀스의 요소가 조건을 만족하는지 여부를 결정합니다.

var numbers = new[]{1,2,3};

numbers.All(n => n % 2 == 0); // returns false
numbers.Any(n => n % 2 == 0); // returns true

7

이 링크 에 따르면

모두 – 하나 이상의 일치를 확인합니다

모두 – 모두 일치하는지 확인


1
당신은 옳지 만 주어진 컬렉션에 대해 동시에 멈 춥니 다. 조건이 실패하면 모든 구분 및 조건 자와 일치하면 모든 구분이 해제됩니다. 따라서 기술적으로는 예외적으로 다른 점이 없습니다
— WPFKK

6

다른 답변에서 잘 설명했듯이 성능에 관한 것이 아니라 명확성에 관한 것입니다.

두 옵션 모두에 대해 광범위하게 지원됩니다.

if (!acceptedValues.Any(v => v == someValue))
{
    // exception logic
}

if (acceptedValues.All(v => v != someValue))
{
    // exception logic
}

그러나 이것이 더 광범위한 지원을 얻을 수 있다고 생각 합니다 .

var isValueAccepted = acceptedValues.Any(v => v == someValue);
if (!isValueAccepted)
{
    // exception logic
}

부울을 계산하기 전에 부울을 계산하고 이름을 지정하는 것만으로도 내 마음에 이것을 많이 알 수 있습니다.


3

당신이 한 번 봐 걸릴 경우 Enumerable에서 소스 당신의 구현 것을 볼 수 Any와 All아주 가깝습니다 :

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (predicate(element)) return true;
    }
    return false;
}

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null) throw Error.ArgumentNull("predicate");
    foreach (TSource element in source) {
        if (!predicate(element)) return false;
    }
    return true;
}

유일한 차이점은 부울 부정에 속하기 때문에 한 방법이 다른 방법보다 훨씬 빠를 수있는 방법은 없으므로 잘못된 성능 승리보다 가독성을 선호합니다.

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