2011-09-21 4 views
0

A previous question은 IEnumerable과 null 값의 빈 컬렉션 대신 빈 컬렉션을 사용하는 규칙에 대해 설명합니다. 많은 실수가 자주 발생하는 null 검사를 수행하지 않는 것이 좋습니다.null 배열과 IEnumerable <T>

하지만 답변을 통해 문제가 해결되지는 않습니다. 여러 번 나는 입니다.은 특히 배열이 외부 메소드에서 반환 될 때 null 값을 처리해야합니다. 예 :

(foreignObj.GetPeople() ?? Enumerable.Empty<Person>()) 
    .Where(p => p.Name != "John") 
    .OrderBy(p => p.Name) 
    .Take(4); 

나는 약간의 가독성을 향상시키는 도우미 방법을 작성했습니다. 결과

public class SafeEnumerable 
{ 
    public static IEnumerable<T> From<T>(T[] arr) 
    { 
     return arr ?? Enumerable.Empty<T>(); 
    } 
} 

:

SafeEnumerable.From(foreignObj.GetPeople()) 
    .Where(p => p.Name != "John") 
    .OrderBy(p => p.Name) 
    .Take(4); 

나는이 상관 없어,하지만 난 더 나은 아이디어를 찾고 있어요. 이미 있어야 할 무언가를 추가하고있는 것처럼 보입니다.

+5

()'당신도 필요가 없습니다 : 그것은 더 관용적, LINQy 방식으로 사용할 수 있도록

당신은 아마 그것을 확장 방법을 만들 수 널 체크 또는'?? '연산자. 이 메소드는 파일이 발견되지 않으면 null을 리턴하지 않는다는 것을 알고, 빈'FileInfo []'배열을 리턴합니다. 그래서 당신의 연결된 질문에 대한 대답도 받아 들일 수 있습니다. – BoltClock

+1

나는'EmptyIfNull '확장 메소드 인'foo.EmptyIfNull() ....'만 사용 했으니 까. – Jon

+0

수정 해 주셔서 고마워. 대신 가상의 예제를 포함하도록 수정했다. –

답변

2

문제가있는 곳은 컬렉션 (IEnumerable<T>)입니다. 컬렉션의 값이 null인지 항상 확인 중이면 원본을 수정해야합니다. 예를 들어 : 사용자가 발견되지 않을 때 그것이 null 반환되면

public User GetUser(long id) { } 
public List<User> GetUsers(long companyId) { } 

첫 번째 방법은 null 반환 값 발견하지 수단을 의미한다. 그러나 두 번째 방법은 정상적인 상황에서는 절대로 null을 반환해서는 안됩니다. 사용자가 없으면 null 값 대신 이 아닌이라는 빈 목록이 반환되어야합니다. 그리고 귀하의 질문에 주어진 예제를 directoryInfo.GetFiles("*.txt")txt 파일이 발견되지 않으면 null을 반환 믿지 않아 대신 빈 컬렉션을 반환해야합니다.

+0

감사합니다. GetFiles가 너무 어쨌든 일반적인 질문을 수정 한 것으로 착각했습니다. 그러나, 그것은 특히 변경할 수없는 외국 방법에 관한 것입니다. –

+0

@ CÅdahl : 내가 묻는 질문에 대답하고 있다고 생각한다. :) –

+0

나는 목록 부분에 동의하지만'GetUser'는 사용자를 얻거나, 시도해 봐야한다고 생각한다. 예외를 throw하지 않고 실패하는 getter 스타일의 메서드를 작성하려면 'public bool TryGetUser (long id, out User result) {}'서명이 있어야합니다. –

-1

불행하게도 나는 내장 된 것이 아무것도 없다고 생각합니다. 당신은 자신을 반복하지 않는 :

foreach(var item in (GetUsers() ?? new User[0])) // ... 

메모리와 wastefuly 변두리 작은 약간 '더 나은'구현 (C# 컴파일러가 yield return sytnax에 대해 생성하는 것과 예를 복용) :

class SafeEnumerable<T> : IEnumerable<T>, IEnumerator<T> 
{ 
    private IEnumerable<T> _enumerable; 
    public SafeEnumerable(IEnumerable<T> enumerable) { _enumerable = enumerable; } 

    public IEnumerator<T> GetEnumerator() 
    { 
     if (_enumerable == null) 
      return this; 
     else 
      return _enumerable.GetEnumerator(); 
    } 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } 

    public T Current { get { throw new InvalidOperationException(); } } 
    object System.Collections.IEnumerator.Current { get { throw new InvalidOperationException(); } } 

    public bool MoveNext() { return false; } 
    public void Reset() { } 
    public void Dispose() { } 
} 
+1

'Enumerable.Empty'를 방금 다시 작성했습니다. http://msdn.microsoft.com/en-us/library/bb341042.aspx – LukeH

+0

LukeH가 말했듯이 Enumerable.Empty가 아닌가요? –

+0

@LukeH d' oh, 매일 새로운 것을 배웁니다. SafeEnumerable은 여전히 ​​장점이 있습니다. –

2

IEnumerable에 대한 일련의 확장 메서드를 만들었습니다. 첫 번째는 EmptyIfNull입니다.

예 :

public static class EnumerableExtensions { 

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

} 

이 제가 약간 짧은 코드를 할 수 있도록

var q = foreignObj.GetPeople().EmptyIfNull().Where(p=>p.Name != "John").OrderBy(p => p.Name).Take(4); 

내가 다음

을 읽을 수/쉽게 입력 할 "안전"확장 기능을 추가 할 수 있습니다 예를 들어,

public static IEnumberable<T> SafeWhere<T>(this collection<T> source,Func<T,bool> predicate) { 
    return source==null ? Enumerable.Empty<T>() : source.Where(predicate); 
} 

당신이 다음 접근 방식은 의미가 널하게 반환하는 방법을 해결하기 위해 소스를 수정할 수없는 경우

var q = foreignObj.GetPeople().SafeWhere(p=>p.Name != "John").OrderBy(p => p.Name).Take(4); 
+0

나는 LukeH의 대답과 같은 예약으로 이것을 좋아한다. 또한 안전 접두사 래퍼는 매우 임의적으로 보입니다. –

1

을 제공. DirectoryInfo.GetFiles`의 특정 경우

var query = foreignObj.GetPeople() 
         .AsNonNullEnumerable() 
         .Where(p => p.Name != "John") 
         .OrderBy(p => p.Name) 
         .Take(4); 

// ... 

public static class EnumerableExtensions 
{ 
    public static IEnumerable<T> AsNonNullEnumerable<T>(this IEnumerable<T> source) 
    { 
     return source ?? Enumerable.Empty<T>(); 
    } 
} 
+0

기술적으로는 괜찮지 만 일관성이없는 것처럼 보입니다. 널 값에 확장 메소드를 호출하는 동안 ** 캔 ** 일, 그것은 LINQ의 많은 확장 방법에 대해 (그들은 던져 결정)하지 않습니다. 그것은 실제로 질문의 전제입니다. :) –

관련 문제