foreach 루프에서 null 확인


97

다음을 수행하는 더 좋은 방법이 있습니까?
루프를 진행하기 전에 file.Headers에서 null이 발생하는지 확인해야합니다.

if (file.Headers != null)
{
  foreach (var h in file.Headers)
  {
   //set lots of properties & some other stuff
  }
}

요컨대 내 코드에서 발생하는 들여 쓰기 수준으로 인해 if 내부에 foreach를 작성하는 것이 약간 추악 해 보입니다.

평가할 것입니다

foreach(var h in (file.Headers != null))
{
  //do stuff
}

가능한?


3
당신은 여기에 모습을 가질 수 있습니다 stackoverflow.com/questions/6937407/...
아드리안 Fâciu

stackoverflow.com/questions/872323/… 은 또 다른 아이디어입니다.
weismat

1
@AdrianFaciu 완전히 다른 것 같아요. 질문은 for-each를 수행하기 전에 컬렉션이 null인지 먼저 확인합니다. 링크는 컬렉션의 항목이 null인지 확인합니다.
rikitikitik


1
C # 8은 단순히 어떤 종류의 null 조건부 foreach를 가질 수 있습니다. 즉, 다음과 같은 구문 : foreach? (var i in collection) {} 나는 이것을 정당화하기에 충분히 일반적인 시나리오라고 생각하며, 여기에서 의미가있는 언어에 최근 null 조건부 추가를 감안할 때?
mms

답변:


124

룬의 제안에 약간의 외형을 추가 한 것처럼 자신 만의 확장 방법을 만들 수 있습니다.

public static IEnumerable<T> OrEmptyIfNull<T>(this IEnumerable<T> source)
{
    return source ?? Enumerable.Empty<T>();
}

그런 다음 다음과 같이 작성할 수 있습니다.

foreach (var header in file.Headers.OrEmptyIfNull())
{
}

취향에 따라 이름 변경 :)


80

file.Headers의 요소 유형이 T라고 가정하면 다음을 수행 할 수 있습니다.

foreach(var header in file.Headers ?? Enumerable.Empty<T>()){
  //do stuff
}

file.Headers가 null 인 경우 T의 빈 열거 형이 생성됩니다. 파일 유형이 내가 소유 한 유형 인 경우 Headers대신 getter를 변경하는 것이 좋습니다. null는 알 수없는 값이므로 가능한 경우 null을 "요소가 없음을 알고 있습니다"로 사용하는 대신 null을 실제로 (/ 원래) "요소가 있는지 모르겠습니다"로 해석해야 할 때 빈 집합을 사용하여 표시합니다. 세트에 요소가 없다는 것을 알고 있습니다. null 검사를 자주 수행 할 필요가 없기 때문에 더 건조 해집니다.

Jons 제안에 대한 후속 조치로 편집 하면 위 코드를 다음과 같이 변경하는 확장 메서드를 만들 수도 있습니다.

foreach(var header in file.Headers.OrEmptyIfNull()){
  //do stuff
}

getter를 변경할 수없는 경우에는 오퍼레이션에 이름 (OrEmptyIfNull)을 부여하여 의도를보다 명확하게 표현하므로 제가 선호하는 방법입니다.

위에서 언급 한 확장 방법은 옵티마이 저가 감지 할 수없는 특정 최적화를 만들 수 있습니다. 특히 메서드 오버로딩을 사용하여 IList와 관련된 항목을 제거 할 수 있습니다.

public static IList<T> OrEmptyIfNull<T>(this IList<T> source)
{
    return source ?? Array.Empty<T>();
}

@kjbartel의 대답 ( "stackoverflow.com/a/32134295/401246")은 다음과 같은 것이 아니기 때문에 최상의 솔루션입니다. a) null전체 루프를 LCD로 저하시키는 성능 저하를 수반합니다 IEnumerable<T>. b) 모든 프로젝트에 확장 메서드를 추가해야하거나 c) null IEnumerables(cuz null는 N / A를 의미하는 반면, 빈 목록은 적용 가능하지만 현재는 존재 함을 의미 합니다.)로 시작 하는 것을 피해야합니다 (Pffft! Puh-LEAZE! SMH.). 음, 비어 있습니다!, 즉, 직원은 판매가 아닌 경우 N / A 인 커미션을 가질 수 있고, 수익을 얻지 못한 경우 판매를 위해 비어있는 커미션을 가질 수 있습니다.
Tom

@Tom은 하나의 null 검사를 제외 하고 열거자가 null이 아닌 경우에 대한 패널티가 없습니다. 열거 가능 항목이 null이 아닌지 확인하면서 해당 검사를 피하는 것은 불가능합니다. 위의 코드 IEnumerableforeach요구 사항 보다 더 제한적 이지만 List<T>링크하는 답변 의 요구 사항보다 덜 제한적인에 대한 헤더가 필요합니다 . 열거 형이 null인지 여부를 테스트하는 것과 동일한 성능 저하가 있습니다.
Rune FS

나는 kjbartel의 답변과 동일한 스레드에있는 Vlad Bezden의 답변에 대한 Eric Lippert의 의견에 "LCD"문제를 기반으로했습니다. "@CodeInChaos : 아, 이제 요점이 보입니다. 컴파일러가"foreach "가 반복되고 있음을 감지 할 수있을 때 List <T> 또는 배열을 통해 값 형식 열거자를 사용하거나 실제로 "for"루프를 생성하도록 foreach를 최적화 할 수 있습니다. 목록이나 빈 시퀀스에 대해 강제로 열거 할 경우 ""로 돌아 가야합니다. 가장 낮은 공통 분모 "codegen, 어떤 경우에는 더 느리고 더 많은 메모리 압력을 생성 할 수 있습니다 ....". 필요에 동의합니다 List<T>.
Tom

@tom 대답의 전제는 file.Headers가 IEnumerable <T> 인 경우 컴파일러가 최적화를 수행 할 수 없다는 것입니다. 그러나 이것을 피하기 위해 확장 방법 솔루션을 확장하는 것은 다소 간단합니다. 편집 참조
Rune FS

19

솔직히, 나는 조언한다 : 그냥 null시험을 빨아 라 . null테스트는 단지brfalse 또는 brfalse.s; 다른 모든 (불필요한 시험, 과제, 여분의 메소드 호출, 훨씬 더 많은 작업을 포함하는 것입니다 GetEnumerator(), MoveNext(), Dispose()반복자에, 등).

if테스트는 간단하고 분명하고 효율적입니다.


1
당신은 흥미로운 점을 지적하고 있지만 Marc는 현재 코드의 들여 쓰기 수준을 줄이는 방법을 찾고 있습니다. 그러나 성능을 기록해야 할 때 귀하의 의견을 기억할 것입니다.
에미넴

3
제가이 질문을하고 성능 향상을 구현해야하는 몇 년 후이 Marc ..에 대해 간단히 메모 해 주시면 귀하의 조언이 매우 유용했습니다. 감사합니다
Eminem

13

반복 전의 "if"는 괜찮지 만 "예쁜"의미론 중 일부는 코드를 읽기 어렵게 만들 수 있습니다.

어쨌든 들여 쓰기로 인해 방해가되는 경우 다음을 확인할 if를 변경할 수 있습니다.

if(file.Headers == null)  
   return;

그리고 headers 속성에 참 값이있을 때만 foreach 루프에 도달하게됩니다.

내가 생각할 수있는 또 다른 옵션은 foreach 루프 내부에서 null 통합 연산자를 사용하고 null 검사를 완전히 피하는 것입니다. 견본:

List<int> collection = new List<int>();
collection = null;
foreach (var i in collection ?? Enumerable.Empty<int>())
{
    //your code here
}

(컬렉션을 실제 개체 / 유형으로 대체)


if 문 외부에 코드가 있으면 첫 번째 옵션이 작동하지 않습니다.
rikitikitik

동의합니다. if 문은 코드 미화를 위해 새 목록을 만드는 것에 비해 구현하기 쉽고 저렴합니다.
Andreas Johansson

10

사용 널 조건 연산자 빠르게 표준 foreach 루프에 비해 작동하고 foreach는 ()를.
하지만 컬렉션을 List로 캐스팅해야합니다.

   listOfItems?.ForEach(item => // ... );

4
이 솔루션이 작동하는 이유를 코드 한 줄이 아닌 명시 적으로 설명하여 답변에 대한 설명을 추가하십시오
LordWilmore

제 경우에 가장 적합한 솔루션
Josef Henn

3

이 시나리오에 대해 멋진 작은 확장 방법을 사용하고 있습니다.

  public static class Extensions
  {
    public static IList<T> EnsureNotNull<T>(this IList<T> list)
    {
      return list ?? new List<T>();
    }
  }

Headers가 목록 유형이므로 다음을 수행 할 수 있습니다.

foreach(var h in (file.Headers.EnsureNotNull()))
{
  //do stuff
}

1
??연산자 를 사용하고 return 문을 다음으로 단축 할 수 있습니다.return list ?? new List<T>;
Rune FS

1
@wolfgangziegler, 내가 올바르게 이해한다면 null샘플 의 테스트 file.Headers.EnsureNotNull() != null가 필요하지 않으며 심지어 잘못 되었습니까?
Remko Jansen 2012

0

어떤 경우에는 기본 컬렉션 생성자가 빈 인스턴스를 반환한다고 가정하고 약간 다른 일반적인 변형을 선호합니다.

이 방법의 이름을 지정하는 것이 좋습니다 NewIfDefault. 컬렉션에만 유용 할 수 있으므로 유형 제약 IEnumerable<T>이 중복 될 수 있습니다.

public static TCollection EmptyIfDefault<TCollection, T>(this TCollection collection)
        where TCollection: class, IEnumerable<T>, new()
    {
        return collection ?? new TCollection();
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.