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()첫 번째 항목에 전달 된 술어 평가합니다로 trueAll()의 술어 평가하여 첫 번째 항목에를 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에서 소스 당신의 구현 것을 볼 수 AnyAll아주 가깝습니다 :

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.