2014-12-16 3 views
1

나는 다음과 같은 LINQ 문이 : 그룹 2는 NullReferenceException이에게 나에게 이것이 해결하기연합 컬렉션

한 가지 방법을 던질 것이다 null의 경우

List<Type> allTypes = group.GetTypes().Union(group2.GetTypes()).ToList(); 

경우가있을 수 있습니다를 전에 NULL 체크, 같은 것을 수행 할 수

if (group2 != null) 
{ 
    List<Type> allTypes = group.GetTypes().Union(group2.GetTypes()).ToList(); 
} 
else 
{ 
    List<Type> allTypes = group.GetTypes(); 
} 

하지만 문제는 다른 종류의 많은 유사한 과제를 가지고 t의 각 진술 경우 수행하지 않는다는 것입니다 이러한 방법으로 옷자락하지만 오히려 한 줄에 NULL 체크를 넣어 것, 같은 :

List<Type> allTypes = group.GetTypes().Union((if group2 != null)group2.GetTypes()).ToList(); 

그러나 LINQ와 함께 할 방법을 잘하지.

+0

다가오는'? .' 연산자를 너무 많이 예상하게 만드는 이러한 질문이 있습니다. – Servy

+0

'group2'가 결코 null이되지 않도록 디자인을 변경하는 것이 좋습니다. – CodesInChaos

답변

4

여기있는 문제는 TSource가 null이 아닙니다. 소스를 가져 오려는 객체가 null입니다 (group2).

언제든지 Enumerable.Empty을 사용하여 마법 하나의 라이너를 저장할 수 있습니다.

List<Type> allTypes = group.GetTypes().Union(group2 != null ? group2.GetTypes() : Enumerable.Empty<Type>()).ToList(); 

또는 당신은 재사용 Union 과부하 사용할 수 있습니다

public static IEnumerable<T> Union<T>(this IEnumerable<IEnumerable<T>> source) 
{ 
    var set = new HashSet<T>(); 
    foreach (var s in source) 
    { 
     foreach (var item in s) 
     { 
      if (set.Add(item)) 
       yield return item; 
     } 
    } 
} 

그런 다음 코드에 회전을 :

var allTypes = new [] { group, group2 }.Where(x => x != null).Select(x => x.GetTypes()).Union().ToList(); 

이 방법의 장점은 두 개 이상의 시퀀스를 가질 수 있다는 것입니다 조합을 형성.

+0

당신이 가지고있는'Union' 메소드는'HashSet'을 생성하고 그것을 가진 모든 시퀀스를 결합함으로써 눈에 띄게 더 효율적이라는 것에주의하십시오. 그대로, 중간 데이터 세트를 나타 내기 위해 해시 세트를 끊임없이 구축하고 버리고 있습니다. – Servy

+0

성능에 대해 옳은 동안,'HashSet'은 시퀀스를 재정렬하는 부작용이 있습니다. Enumerable.Union은 먼저 첫 번째 시퀀스에서 (순서대로) 요소를 반환하고 두 번째 시퀀스에서 요소를 반환합니다. – gwiazdorrr

+1

그 행동을 쉽게 에뮬레이션 할 수 있습니다. 본질적으로 코드는'Union '의 소스 코드와 똑같이 보일 것입니다. 단, 각 세트에 대해 코드를 한 번 수행하는 대신 두 세트를'foreach '해야합니다. foreach (var item in sequence) if (set.Add (item)) yield (오류 체크와 세트 생성 외의 코드) 반환 항목; – Servy

2

null 체크를 한 행에 입력했을 때 가장 좋은 점은 ternary operator입니다.

List<Type> allTypes = group2 == null ? 
    group.GetTypes() 
    : group.GetTypes().Union(group2.GetTypes()).ToList(); 
+0

좋은 해결책이지만, @ usr과 같은 것을 할 수 있기를 바랬습니다. – inside

+0

@Stanislav 제 대답에 나와있는 것처럼 C# 6.0이 나올 수 있습니다. 현재 그것을 할 방법이 없습니다. – Servy

2

이 경우 일반적으로 새로운 확장 방법을 만듭니다. 예컨대 :

public static IEnumerable<T> SafeUnion<T>(
    this IEnumerable<T> source1, IEnumerable<T> source2) 
{ 
    return source1 != null ? 
     (source2 != null ? source1.Union(source2) : source1) : source2; 
} 

또는 특정 논리가 귀하의 경우에 가장 적합한 무엇이든합니다 (위의 수 중 하나는 null 열거 ... 당신은 예를 들어, 단지 두 번째를 허용 할 수 있습니다).

일부 curmudgeons는 OP가 자신의 필요에 따라이 아이디어를 적용 할 수 없다고 생각할 수 있습니다. 나는 아마도 그가 아무런 문제가 없을 것이라고 생각합니다. 그리고 변수에 대한 선언이 없으면 그것이 어떻게 생겼는지 정확하게 나타낼 수 없습니다. 물론

public static List<Type> SafeUnion(this Group group1, Group group2) 
{ 
    return (group2 != null ? 
      group1.GetTypes().Union(group2.GetTypes()) : group1.GetTypes(); 
} 

에서, Group 유형이 변수의 유형이 실제로 무엇으로 대체 할 필요가 :하지만 뭔가 이런 비트 될 것이다. 이 예에서는 group1도 null이 될 수 없습니다. 필요한 경우 아마도 독자가 독자적으로 변경 사항을 파악할 수 있습니다.

+0

그건 완전히 이해가됩니다. LINQ는 이미 확장 메서드이므로 더 많이 추가하십시오. – ryanyuyu

+1

그러나 문제는 해결되지 않습니다. 문제는 시퀀스가 ​​null이 아니며 'group2' 객체가 null입니다. 또한,'SafeUnion'은 널 (null)을 리턴 할 수 있습니다. 이는 LINQish가 아 U니다. – gwiazdorrr

+0

필자가 작성한 것처럼이 예는 OP의 필요에 따라 조정될 수 있습니다. 불행하게도, OP는 완전한 코드 예제를 제공하지 않았고,'group'과'group2' 변수를 적절하게 선언하지 않으면 정확히 일치하는 코드 예제를 작성할 수 없습니다. 'null '을 반환하는 것과 마찬가지로, 음 ... 처음에는 확장 메서드에서 null을 허용하는 "LINQ-ish"가 아닙니다. –

3

null 매개 변수를 지원할 수있는 그룹 유형을 얻는 방법은 여기에 필요합니다.이 작성하는 매우 간단한 방법입니다 :

public static IEnumerable<Type> MyGetTypes(Group group) 
{ 
    if(group == null) 
     return Enumerable.Empty<Type>(); 
    else 
     return group.GetTypes(); 
} 

이제 원래의 코드를 작성할 수 있습니다 (당신이 원하는 경우에 당신은 그것을 확장 방법을 만들 수있다)와 같은 :

var allTypes = MyGetTypes(group).Union(MyGetTypes(group2)).ToList(); 

우리가 원한다면,이 방법을 매우 구체적으로 만드는 대신에 이것을 일반화 할 수도 있습니다.

public static TResult Use<TSource, TResult>(TSource source, 
    Func<TSource, TResult> selector, 
    TResult defaultValue = default(TResult)) 
{ 
    if (source == null) 
     return defaultValue; 
    else 
     return selector(source); 
} 

이것은 우리가 쓸 수 것 :

var allTypes = group.GetTypes() 
    .Union(group2.Use(g => g.GetTypes(), Enumerable.Empty<Type>())) 
    .ToList(); 

C# 6.0 나오고 우리는 당신이 또한과 같이 코드를 쓸 수하려는 ?. 운영자에 대한 액세스 권한을 얻을 때 :

var allTypes = group.GetTypes() 
    .Union(group2?.GetTypes() ?? Enumerable.Empty<Type>()) 
    .ToList(); 

이렇게하면 null 그룹을 throw하는 것이 아니라 null 형식의 컬렉션으로 전파 할 수 있습니다. 그 다음 null 값을 빈 콜렉션으로 대체 할 수 있습니다.이 콜렉션은 Union을 지원합니다. 이 연산자는 다소 우리의 Use 메서드의 내장 버전이지만 람다를 사용하지 않아도되므로 훨씬 간결하게 만들 수 있습니다.