foreach (항목의 T 항목) 전에 if (items! = null)이 불필요합니까?


103

나는 종종 다음과 같은 코드를 보게됩니다.

if ( items != null)
{
   foreach(T item in items)
   {
        //...
   }
}

기본적으로 if조건은 null이 아닌 foreach경우에만 블록이 실행 되도록합니다 items. if조건이 정말로 필요한지 궁금 foreach합니다 items == null.

내 말은, 그냥 쓸 수 있을까요

foreach(T item in items)
{
    //...
}

itemsnull 인지 아닌지 걱정 하지 않고? 는 IS if조건 불필요한은? 아니면 이것은 유형 에 따라 달라 items지거나 어쩌면에 달려 T있습니까?



1
@ kjbartel의 대답 ( "에서 stackoverflow.com/a/32134295/401246은 그렇지 않기 때문에"최선의 해결책은있다 : A)는 (도하지 않을 때의 성능 저하를 수반 null의 LCD에 전체 루프를 일반화) Enumerable사용으로 ( ??것 ), b) 모든 프로젝트에 확장 메서드를 추가해야합니다. 또는 c) null IEnumerables (Pffft! Puh-LEAZE! SMH.)로 시작 하는 것을 피해야 합니다 (cuz, nullN / A를 의미하는 반면 빈 목록은 적용됨을 의미합니다. 현재는 비어 있습니다 !, 즉 Empl.은 판매가 아닌 경우 N / A 인 커미션을 가질 수 있고, 수익을 얻지 못한 경우 판매를 위해 비어있는 커미션을 가질 수 있습니다.

답변:


115

(items! = null) 여부를 확인해야합니다. 그렇지 않으면 NullReferenceException이 발생합니다. 그러나 다음과 같이 할 수 있습니다.

List<string> items = null;  
foreach (var item in items ?? new List<string>())
{
    item.Dump();
}

하지만 성능을 확인할 수 있습니다. 그래서 나는 여전히 if (items! = null)을 먼저 갖는 것을 선호합니다.

Eric의 Lippert 제안에 따라 코드를 다음과 같이 변경했습니다.

List<string> items = null;  
foreach (var item in items ?? Enumerable.Empty<string>())
{
    item.Dump();
}

31
귀여운 아이디어; 빈 배열은 더 적은 메모리를 소비하고 더 적은 메모리 압력을 생성하기 때문에 선호됩니다. Enumerable.Empty <string>은 생성하고 재사용하는 빈 배열을 캐시하기 때문에 훨씬 더 선호됩니다.
Eric Lippert 2011-06-23

5
두 번째 코드가 더 느릴 것으로 예상합니다. 시퀀스를로 퇴화시키고 IEnumerable<T>, 차례로 인터페이스에 대한 열거 자로 저하되어 반복 속도가 느려집니다. 내 테스트는 int 배열에 대한 반복에 대한 요소 5 저하를 보여주었습니다.
CodesInChaos

11
@CodeInChaos : 일반적으로 빈 시퀀스를 열거하는 속도가 프로그램의 성능 병목 현상이라는 것을 알고 있습니까?
Eric Lippert

14
빈 시퀀스의 열거 속도뿐만 아니라 전체 시퀀스의 열거 속도도 감소시킵니다. 시퀀스가 충분히 길면 문제가 될 수 있습니다. 대부분의 코드에서 우리는 관용적 코드를 선택해야합니다. 그러나 언급 한 두 가지 할당은 더 적은 경우에 성능 문제가 될 것입니다.
CodesInChaos 2011-06-23

15
@CodeInChaos : 아, 이제 요점이 보입니다. 컴파일러가 "foreach"가 List <T> 또는 배열을 반복하고 있음을 감지하면 foreach를 최적화하여 값 형식 열거자를 사용하거나 실제로 "for"루프를 생성 할 수 있습니다. 목록 이나 빈 시퀀스에 대해 강제로 열거해야하는 경우 "가장 낮은 공통 분모"코드 젠으로 돌아 가야합니다. 일부 경우에는 속도가 느려지고 더 많은 메모리 압력이 발생할 수 있습니다. 이것은 미묘하지만 훌륭한 포인트입니다. 물론 이야기의 교훈은 항상 그렇듯이 성능 문제가있는 경우 프로필을 작성하여 실제 병목 현상이 무엇인지 알아내는 것입니다.
Eric Lippert

68

C # 6을 사용하면 새로운 null 조건 연산자를 List<T>.ForEach(Action<T>)(또는 자체 IEnumerable<T>.ForEach확장 메서드) 와 함께 사용할 수 있습니다 .

List<string> items = null;
items?.ForEach(item =>
{
    // ...
});

우아한 대답. 감사!
Steve

2
그렇지 않은 때문 최고의 솔루션입니다 :조차하지 않을 때 (의 성능 저하가)을 포함 null)의 LCD에 전체 루프를 일반화 Enumerable사용으로 ( ??것), b)는 모든 프로젝트에 확장 메서드를 추가하는 필요를, 또는 c ) 방지 요구 null IEnumerable들, 사촌 (Pffft! 푸후 - LEAZE! SMH가. ()로 시작하는 nullN / A를 의미 빈리스트 수단, 그것의 APPL 반면,.하지만 현재 잘되고 비우 !, 즉 EMPL가.위원회의가있을 수 있습니다 판매가 아닌 경우 N / A, 수익이없는 경우 판매의 경우 비어 있음).
Tom

6
@ 톰 : 그것은 그 가정 itemsA는 List<T>오히려 그냥보다,하지만 IEnumerable<T>. (또는 사용자 정의 확장 방법을 사용하십시오. 당신이 원하지 않는다고 말한 ...) 또한 기본적으로 특정 답변을 좋아한다고 말하는 11 개의 주석을 추가 할 가치가 없다고 말하고 싶습니다.
Jon Skeet

2
@Tom : 앞으로는 그렇게하지 않는 것이 좋습니다. 귀하의 의견에 동의하지 않는 모든 사람이 귀하의 의견에 모두 의견을 추가했다고 상상해보십시오 . (여기에 내 답장을 11 번 작성했다고 상상해보세요.) 이것은 단순히 Stack Overflow의 생산적인 사용이 아닙니다.
Jon Skeet

1
또한 표준 대 델리게이트를 호출하는 성능 저하가있을 것이라고 가정합니다 foreach. 특히 내가 생각하는 목록의 경우 for루프 로 변환됩니다 .
kjbartel

37

여기서 진정한 의미 는 시퀀스가 ​​처음에는 거의 null이 될 수 없다는 것 입니다. 시퀀스가 있으면 절대 null이 아닌 모든 프로그램에서 불변으로 만드십시오. 항상 빈 시퀀스 또는 다른 정품 시퀀스로 초기화됩니다.

시퀀스가 null이 아닌 경우 분명히 확인할 필요가 없습니다.


1
WCF 서비스에서 시퀀스를 가져 오면 어떨까요? null 일 수도 있지요?
Nawaz

4
@Nawaz : 빈 시퀀스가되도록 null 시퀀스를 반환하는 WCF 서비스가 있다면 버그로보고합니다. 즉, 버그가있을 수있는 서비스의 잘못된 형식의 출력을 처리해야하는 경우, 예, null을 확인하여 처리해야합니다.
Eric Lippert 2011-06-23

7
물론 null과 empty가 완전히 다른 것을 의미하지 않는 한. 때로는 시퀀스에 유효합니다.
구성자

@Nawaz 빈 컬렉션 대신 null을 반환하는 DataTable.Rows는 어떻습니까? 버그일까요?
Neil B

@ kjbartel의 대답 ( "에서 stackoverflow.com/a/32134295/401246은 그렇지 않기 때문에"최선의 해결책은있다 : A)는 (도하지 않을 때의 성능 저하를 수반 null의 LCD에 전체 루프를 일반화) Enumerable사용으로 ( ??것 ), b) 모든 프로젝트에 확장 메서드를 추가해야합니다. 또는 c) null IEnumerables (Pffft! Puh-LEAZE! SMH.)로 시작 하는 것을 피해야 합니다 (cuz, nullN / A를 의미하는 반면 빈 목록은 적용됨을 의미합니다. 현재는 비어 있습니다 !, 즉 Empl.은 판매가 아닌 경우 N / A 인 커미션을 가질 수 있고, 수익을 얻지 못한 경우 판매를 위해 비어있는 커미션을 가질 수 있습니다.
Tom

10

실제로 @Connect에 대한 기능 요청이 있습니다. http://connect.microsoft.com/VisualStudio/feedback/details/93497/foreach-should-check-for-null

그리고 응답은 매우 논리적입니다.

대부분의 foreach 루프는 null이 아닌 컬렉션을 반복 할 의도로 작성되었다고 생각합니다. null을 통해 반복을 시도하면 코드를 수정할 수 있도록 예외가 발생해야합니다.


장단점이있는 것 같아서 처음에 설계된 그대로 유지하기로 결정했습니다. 결국, foreach는 단지 구문상의 설탕 일뿐입니다. items.GetEnumerator ()를 호출했다면 항목이 null 인 경우에도 충돌 할 수 있으므로 먼저 테스트해야합니다.
마리우스 반 실라

6

당신은 항상 null 목록으로 그것을 테스트 할 수 있습니다 ...하지만 이것은 msdn 웹 사이트에서 찾은 것입니다.

foreach-statement:
    foreach   (   type   identifier   in   expression   )   embedded-statement 

식에 null 값이 있으면 System.NullReferenceException이 throw됩니다.


2

슈퍼 플로 스가 아닙니다. 런타임시 항목은 IEnumerable로 캐스팅되고 해당 GetEnumerator 메서드가 호출됩니다. 그러면 실패 할 항목의 역 참조가 발생합니다.


1
1) 시퀀스가 ​​반드시 캐스팅되는 것은 아닙니다. IEnumerable2) 던지는 것은 디자인 결정입니다. C #은 null개발자가 좋은 아이디어라고 생각하는지 확인하는 것을 쉽게 삽입 할 수 있습니다 .
CodesInChaos

2

확장 메서드에서 null 검사를 캡슐화하고 람다를 사용할 수 있습니다.

public static class EnumerableExtensions {
  public static void ForEach<T>(this IEnumerable<T> self, Action<T> action) {
    if (self != null) {
      foreach (var element in self) {
        action(element);
      }
    }
  }
}

코드는 다음과 같습니다.

items.ForEach(item => { 
  ...
});

항목을 가져 와서 반환하는 메서드를 호출하려는 경우 훨씬 더 간결 할 수 있습니다 void.

items.ForEach(MethodThatTakesAnItem);

1

이게 필요합니다. foreach그렇지 않으면 반복을 설정하기 위해 컨테이너에 액세스 할 때 예외가 발생 합니다.

커버 아래, foreach 사용하는 컬렉션 클래스에 구현 된 인터페이스 반복을 수행 할 수 있습니다. 일반적인 동등한 인터페이스는 여기에 있습니다 .

C # 언어의 foreach 문 (Visual Basic의 경우 for each)은 열거 자의 복잡성을 숨 깁니다. 따라서 열거자를 직접 조작하는 대신 foreach를 사용하는 것이 좋습니다.


1
기술적으로 인터페이스를 사용하지 않는 것처럼 덕 타이핑을 사용합니다. blogs.msdn.com/b/kcwalina/archive/2007/07/18/ducknotation.aspx 인터페이스는 올바른 메서드와 속성이 있는지 확인합니다. 하지만 의도를 이해하는 데 도움이됩니다. 뿐만 아니라 foreach 외부 사용 ...
ShuggyCoUk 2011-06-23

0

컬렉션이 null이면 foreach가 NullReferenceException을 throw하므로 테스트가 필요합니다. 실제로 사용해 보는 것은 매우 간단합니다.

List<string> items = null;
foreach(var item in items)
{
   Console.WriteLine(item);
}

0

두 번째는 NullReferenceException메시지와 함께Object reference not set to an instance of an object.



0

C # 6에서는 다음과 같이 sth를 작성할 수 있습니다.

// some string from file or UI, i.e.:
// a) string s = "Hello, World!";
// b) string s = "";
// ...
var items = s?.Split(new char[] { ',', '!', ' ' }) ?? Enumerable.Empty<string>();  
foreach (var item in items)
{
    //..
}

기본적으로 Vlad Bezden의 솔루션이지만 ?? 표현식은 항상 널이 아니므로 foreach 대괄호 안에이 검사를 두지 않고 foreach에서 살아남는 배열을 생성합니다.

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