null 또는 빈 컬렉션을 반환하는 것이 더 낫습니까?


420

그것은 일반적인 질문입니다 (그러나 C #을 사용하고 있습니다), 가장 좋은 방법 (모범 사례)은 컬렉션을 반환 유형으로 갖는 메소드에 대해 null 또는 빈 컬렉션을 반환합니까?


5
음, CPerkins는 아닙니다. rads.stackoverflow.com/amzn/click/0321545613

3
@CPerkins-그렇습니다. Microsoft 자체의 .NET 프레임 워크 디자인 지침에 명시되어 있습니다. 자세한 내용은 RichardOD의 답변을 참조하십시오.
Greg Beech

51
의미가 "결과를 계산할 수 없음"인 경우에만 null을 반환해야합니다. 널은 "빈"의 의미를 갖지 않아야하며 "누락"또는 "알 수 없음"만 있어야합니다. : 주제에 나의 기사에서 자세한 내용 blogs.msdn.com/ericlippert/archive/2009/05/14/...
에릭 Lippert의

3
Bozho : "any"는 엄청나게 많은 언어입니다. 빈리스트가 null 값과 정확히 동일한 Common Lisp는 어떻습니까? :-)
Ken

9
실제로 "중복"은 컬렉션이 아니라 객체를 반환하는 메서드에 관한 것입니다. 답변이 다른 시나리오입니다.
GalacticCowboy

답변:


499

빈 컬렉션. 항상.

이것은 짜증나 :

if(myInstance.CollectionProperty != null)
{
  foreach(var item in myInstance.CollectionProperty)
    /* arrgh */
}

null컬렉션을 반환하거나 열거 할 때 절대 반환 하지 않는 것이 가장 좋습니다 . 빈 열거 형 / 컬렉션을 항상 반환합니다. 그것은 앞서 말한 넌센스를 방지하고 동료와 클래스의 사용자가 자동차에 알을 낳는 것을 방지합니다.

속성에 대해 이야기 할 때는 항상 속성을 한 번 설정하고 잊어 버리십시오.

public List<Foo> Foos {public get; private set;}

public Bar() { Foos = new List<Foo>(); }

.NET 4.6.1에서는이를 상당히 많이 압축 할 수 있습니다.

public List<Foo> Foos { get; } = new List<Foo>();

열거 형을 반환하는 메서드에 대해 이야기 할 때 빈 열거 형을 쉽게 반환 할 수 있습니다 null...

public IEnumerable<Foo> GetMyFoos()
{
  return InnerGetFoos() ?? Enumerable.Empty<Foo>();
}

Enumerable.Empty<T>()예를 들어, 새로운 빈 컬렉션이나 배열을 반환하는 것보다 사용하는 것이 더 효율적인 것으로 볼 수 있습니다.


24
나는 의지에 동의하지만 "항상"은 약간 과도하다고 생각합니다. 빈 컬렉션은 "0 items"를 의미 할 수 있지만 Null을 반환하면 "no collections all"을 의미 할 수 있습니다. HTML을 구문 분석하는 경우 id = "foo"가있는 <ul>을 찾으면 <ul id = "foo"> </ ul>이 빈 컬렉션을 반환 할 수 있습니다. id = "foo"인 <ul>이 없으면 널 리턴이 더 좋습니다 (예외로이 경우를 처리하지 않는 한)
Patonza

30
항상 "빈 배열을 쉽게 반환 할 수 있는지"에 대한 질문이 아니라 현재 배열에서 빈 배열이 잘못 될 수 있는지 여부에 대한 질문이 아닙니다. 빈 배열은 실제로 null과 마찬가지로 무언가를 의미합니다. 항상 null 대신 빈 배열을 반환해야한다고 말하는 것은 부울 메서드가 항상 true를 반환해야한다고 말하는 것만 큼 잘못 안내됩니다. 가능한 값은 모두 의미를 전달합니다.
David Hedlund

31
실제로 새 Foo [0] 대신 System.Linq.Enumerable.Empty <Foo> ()를 반환하는 것이 좋습니다. 더 명확하고 하나 이상의 메모리 할당을 저장합니다 (적어도 설치된 .NET 구현에서).
Trillian

4
@Will : OP : "컬렉션을 반환 유형으로하는 메서드에 대해 null 또는 빈 컬렉션을 반환합니까? " 나는 여부,이 경우 생각 IEnumerable이나 ICollection그만큼 중요하지 않습니다. 어쨌든, 당신이 유형의 ICollection것을 선택하면 그들은 또한 반환합니다 null... 빈 컬렉션을 반환하고 싶지만 나는 그들이 돌아 오는 것을 만났 null으므로 여기서 언급 할 것이라고 생각했습니다. 열거 가능한 컬렉션의 기본값이 null이 아닌 비어 있다고 말하고 싶습니다. 나는 그것이 민감한 주제라는 것을 몰랐다.
Matthijs Wessels


154

로부터 프레임 워크 디자인 가이드 라인 제 2 판 (페이지 256.) :

컬렉션 속성이나 컬렉션을 반환하는 메서드에서 null 값을 반환하지 마십시오. 빈 컬렉션이나 빈 배열을 대신 반환하십시오.

null을 반환하지 않는 이점에 대한 또 다른 흥미로운 기사가 ​​있습니다 (Brad Abram의 블로그에서 무언가를 찾으려고 노력했으며 기사에 링크되었습니다).

편집 - 에릭 Lippert의 등은 이제 원래의 질문에 댓글을 달았습니다, 나는 또한 싶습니다 그의 뛰어난 기사 링크 .


33
+1. 아주 좋은 이유가없는 한 프레임 워크 디자인 지침을 따르는 것이 항상 가장 좋습니다.

@ 윌, 절대적으로. 그것이 내가 따르는 것입니다. 다른 방법이 필요 없습니다
RichardOD

p, 그게 당신이 따르는 것입니다. 그러나 당신이 의존하는 API가 그것을 따르지 않으면, 당신은 '갇힌';)
Bozho

@ Bozho- yeap. 귀하의 답변은 이러한 프린지 사례에 대한 좋은 답변을 제공합니다.
RichardOD 2009

90

계약구체적인 사례 에 따라 다릅니다 . 일반적으로 빈 컬렉션을 반환하는 것이 가장 좋지만 간혹 ( 드물게 ) :

  • null 좀 더 구체적인 것을 의미 할 수도 있습니다.
  • 귀하의 API (계약)가 강제로를 반환 할 수 있습니다 null.

구체적인 예 :

  • 컨트롤에서 라이브러리의 UI 구성 요소로, 빈 컬렉션이 전달되면 빈 테이블을 렌더링하거나 null이 전달되면 테이블이 전혀 렌더링되지 않을 수 있습니다.
  • Object-to-XML (JSON / 무엇이든) null에서 요소가 누락되었다는 것을 의미하는 반면, 빈 컬렉션은 중복 (및 가능하지 않은)을 렌더링합니다<collection />
  • null을 반환 / 전달해야한다고 명시 적으로 명시하는 API를 사용하거나 구현하고 있습니다.

4
@ Bozho- 여기 몇 가지 흥미로운 예가 있습니다.
RichardOD 2009

1
나는 당신이 다른 사건을 중심으로 코드를 작성해야한다고 생각하지는 않지만 C #에서 'null'을 정의 하는 것은 결코 특정한 것을 의미 하지는 않지만 당신의 요점 이벤트를 보았습니다 . null 값은 "정보 없음"으로 정의되므로 특정 정보를 전달한다는 것은 옥시 모론입니다. 따라서 .NET 가이드 라인에 세트가 실제로 비어 있으면 빈 세트를 반환해야한다고 명시되어 있습니다. null을 반환한다는 말 : "예상 세트가 어디로 갔는지 모르겠습니다"
Rune FS

13
아니요, "요소가 없습니다"가 아니라 "설정이 없음"을 의미합니다
Bozho

예전에는 TSQL을 많이 작성해야한다고 생각했지만 항상 그렇지는 않습니다.

1
Object-To-Xml이있는 부분이 있습니다.
Mihai Caracostea

36

아직 언급되지 않은 또 다른 요점이 있습니다. 다음 코드를 고려하십시오.

    public static IEnumerable<string> GetFavoriteEmoSongs()
    {
        yield break;
    }

C # 언어는이 메서드를 호출 할 때 빈 열거자를 반환합니다. 따라서 언어 디자인 (따라서 프로그래머의 기대)에 따라 빈 컬렉션을 반환해야합니다.


1
기술적으로 이것이 빈 컬렉션을 반환한다고 생각하지 않습니다.
FryGuy

3
@FryGuy-아닙니다. GetEnumerator () 메소드는 (컬렉션과 함께 analagy에 의해) 비어있는 Enumerator를 리턴하는 Enumerable 오브젝트를 리턴합니다. 즉, 열거 자의 MoveNext () 메서드는 항상 false를 반환합니다. 예제 메서드를 호출해도 null이 반환되지 않으며 GetEnumerator () 메서드가 null을 반환하는 Enumerable 개체도 반환되지 않습니다.
Jeffrey L Whitledge

31

빈은 훨씬 더 소비자 친화적입니다.

빈 열거 형을 구성하는 명확한 방법이 있습니다.

Enumerable.Empty<Element>()

2
깔끔한 트릭에 감사드립니다. 나는 바보처럼 빈 List <T> ()를 인스턴스화하고 있었지만 이것은 훨씬 깔끔해 보였으며 아마도 조금 더 효율적입니다.
Jacobs Data Solutions

18

상황에 따라 의미 적으로 올바른 값을 반환해야한다고 생각합니다. "항상 빈 컬렉션을 반환합니다"라는 규칙은 약간 단순 해 보입니다.

예를 들어, 병원 시스템에서 지난 5 년간의 모든 이전 입원 목록을 반환해야하는 기능이 있다고 가정합니다. 고객이 병원에없는 경우 빈 목록을 반환하는 것이 좋습니다. 그러나 고객이 입학 양식의 해당 부분을 비워두면 어떻게됩니까? "빈 목록"과 "답변 없음"또는 "모름"을 구별하려면 다른 값이 필요합니다. 예외를 던질 수는 있지만 반드시 오류 조건은 아니며 정상적인 프로그램 흐름에서 벗어날 수있는 것은 아닙니다.

나는 0과 무응답을 구분할 수없는 시스템에 종종 좌절했습니다. 시스템에서 숫자를 입력하도록 요청한 횟수가 0이고 0을 입력하면이 필드에 값을 입력해야한다는 오류 메시지가 표시됩니다. 방금 했어요 : 0을 입력했습니다! 그러나 대답이없는 것과 구별 할 수 없기 때문에 0을 허용하지 않습니다.


손더스에 대한 답변 :

예, "사람이 질문에 대답하지 않았습니다"와 "답이 0이었습니다"사이에 차이가 있다고 가정합니다. 그것이 내 대답의 마지막 단락의 요점이었습니다. 많은 프로그램들이 "알지 못함"을 공란 또는 제로와 구별 할 수 없으며, 이는 잠재적으로 심각한 결함으로 보입니다. 예를 들어 1 년 정도 전에 집을 쇼핑하고있었습니다. 나는 부동산 웹 사이트에 갔고 ​​가격이 $ 0 인 많은 주택이 나열되었습니다. 나에게 꽤 좋은 소리 : 그들은이 집을 무료로 제공하고 있습니다! 그러나 슬픈 현실은 그들이 가격에 들어 가지 않았다는 것이 확실합니다. 이 경우에, 당신은 말할 수 있습니다. 음, 명백하게 0은 그들이 가격을 입력하지 않았다는 것을 의미합니다. 아무도 집을 무료로주지 않을 것입니다. " 그러나이 사이트는 또한 여러 도시의 평균 주택 가격과 판매 가격을 나열했습니다. 평균에 0이 포함되어 있지 않은지 궁금해 할 수 없으므로 일부 장소의 평균이 잘못되었습니다. 즉, 평균 $ 100,000는 얼마입니까? $ 120,000; "몰라"? 기술적으로 대답은 "모름"입니다. 우리가 실제로보고 싶은 것은 $ 110,000입니다. 그러나 우리가 얻을 수있는 것은 $ 73,333이며, 이는 완전히 잘못되었을 것입니다. 또한 사용자가 온라인으로 주문할 수있는 사이트에서이 문제가 발생한 경우 어떻게해야합니까? (부동산은 아닐 것입니다 만, 다른 많은 제품들에서 그 일을했다고 확신합니다.) "가격이 아직 지정되지 않았습니다"가 "무료"로 해석되기를 정말로 원하십니까? 따라서 일부 장소의 평균이 잘못되었습니다. 즉, 평균 $ 100,000는 얼마입니까? $ 120,000; "몰라"? 기술적으로 대답은 "모름"입니다. 우리가 실제로보고 싶은 것은 $ 110,000입니다. 그러나 우리가 얻을 수있는 것은 $ 73,333이며, 이는 완전히 잘못되었을 것입니다. 또한 사용자가 온라인으로 주문할 수있는 사이트에서이 문제가 발생한 경우 어떻게해야합니까? (부동산은 아닐 것입니다 만, 다른 많은 제품들에서 그 일을했다고 확신합니다.) "가격이 아직 지정되지 않았습니다"가 "무료"로 해석되기를 정말로 원하십니까? 따라서 일부 장소의 평균이 잘못되었습니다. 즉, 평균 $ 100,000는 얼마입니까? $ 120,000; "몰라"? 기술적으로 대답은 "모름"입니다. 우리가 실제로보고 싶은 것은 $ 110,000입니다. 그러나 우리가 아마 얻을 수있는 것은 $ 73,333이며, 이는 완전히 잘못되었을 것입니다. 또한 사용자가 온라인으로 주문할 수있는 사이트에서이 문제가 발생한 경우 어떻게해야합니까? (부동산은 아닐 것입니다 만, 다른 많은 제품들에서 그 일을했다고 확신합니다.) "가격이 아직 지정되지 않았습니다"가 "무료"로 해석되기를 정말로 원하십니까? 그것은 완전히 잘못된 것입니다. 또한 사용자가 온라인으로 주문할 수있는 사이트에서이 문제가 발생한 경우 어떻게해야합니까? (부동산은 아닐 것입니다 만, 다른 많은 제품들에서 그 일을했다고 확신합니다.) "가격이 아직 지정되지 않았습니다"가 "무료"로 해석되기를 정말로 원하십니까? 그것은 완전히 잘못된 것입니다. 또한 사용자가 온라인으로 주문할 수있는 사이트에서이 문제가 발생한 경우 어떻게해야합니까? (부동산은 아닐 것입니다 만, 다른 많은 제품들에서 그 일을했다고 확신합니다.) "가격이 아직 지정되지 않았습니다"가 "무료"로 해석되기를 정말로 원하십니까?

두 가지 기능을 가진 RE, "있는가?" "그렇다면 무엇입니까?" 그렇습니다, 당신은 확실히 그렇게 할 수 있지만, 왜 당신은 원합니까? 이제 호출 프로그램은 하나 대신 두 번 호출해야합니다. 프로그래머가 "any"를 호출하지 않으면 어떻게됩니까? "무엇입니까?" ? 프로그램이 잘못된 0을 반환합니까? 예외를 던져? 정의되지 않은 값을 반환 하시겠습니까? 더 많은 코드, 더 많은 작업 및 더 많은 잠재적 오류를 만듭니다.

내가 볼 수있는 유일한 이점은 임의의 규칙을 준수 할 수 있다는 것입니다. 이 규칙에 따르는 데 따르는 이점이 있습니까? 그렇지 않다면 왜 귀찮게합니까?


Jammycakes에 대한 답변 :

실제 코드의 모양을 고려하십시오. 질문에 C #이 있다고 말했지만 Java를 작성하면 실례합니다. 내 C #은 매우 선명하지 않으며 원칙은 동일합니다.

널 리턴으로 :

HospList list=patient.getHospitalizationList(patientId);
if (list==null)
{
   // ... handle missing list ...
}
else
{
  for (HospEntry entry : list)
   //  ... do whatever ...
}

별도의 기능으로 :

if (patient.hasHospitalizationList(patientId))
{
   // ... handle missing list ...
}
else
{
  HospList=patient.getHospitalizationList(patientId))
  for (HospEntry entry : list)
   // ... do whatever ...
}

실제로 널 리턴이있는 줄이 하나 또는 두 줄이 적기 때문에 호출자에게 더 많은 부담이되지 않습니다.

DRY 문제를 만드는 방법을 모르겠습니다. 전화를 두 번 실행해야하는 것은 아닙니다. 리스트가 존재하지 않을 때 항상 똑같은 일을하고 싶었다면, 호출자가하지 않고 get-list 함수로 처리를 푸시 할 수 있기 때문에 호출자에 코드를 넣는 것은 DRY 위반 일 수 있습니다. 그러나 우리는 거의 항상 똑같은 일을하고 싶지 않습니다. 처리 할 목록이 있어야하는 함수에서 누락 된 목록은 처리를 중단 할 수있는 오류입니다. 그러나 편집 화면에서는 아직 데이터를 입력하지 않은 경우 처리를 중단하고 싶지 않습니다. 데이터를 입력하도록하겠습니다. 따라서 "목록 없음"처리는 발신자 수준에서 어떤 방식 으로든 수행되어야합니다. 그리고 우리가 null을 반환하거나 별도의 함수로 수행하는지 여부는 더 큰 원칙과 아무런 차이가 없습니다.

물론, 호출자가 널을 점검하지 않으면 널 포인터 예외로 인해 프로그램이 실패 할 수 있습니다. 그러나 별도의 "get any"기능이 있고 호출자가 해당 기능을 호출하지 않고 "get list"기능을 맹목적으로 호출하면 어떻게됩니까? 예외가 발생하거나 실패하는 경우 null을 반환하고 확인하지 않은 경우와 거의 동일합니다. 빈 목록을 반환하면 잘못되었습니다. "요소가없는 목록이 있습니다"와 "목록이 없습니다"를 구별하지 못했습니다. 사용자가 가격을 입력하지 않은 경우 가격을 0으로 반환하는 것과 같습니다. 잘못되었습니다.

컬렉션에 추가 속성을 첨부하는 것이 어떻게 도움이되는지 모르겠습니다. 발신자는 여전히 확인해야합니다. null을 확인하는 것보다 낫습니까? 다시 말하지만, 최악의 상황은 프로그래머가 확인을 잊고 잘못된 결과를 제공하는 것입니다.

프로그래머가 "값이 없다"는 의미의 null의 개념에 익숙하다면 null을 반환하는 함수는 놀라운 일이 아닙니다. 별도의 기능을 갖는 것이 "놀라운"문제에 가깝다고 생각합니다. 프로그래머가 API에 익숙하지 않은 경우 데이터없이 테스트를 실행하면 때로는 null을 반환한다는 것을 빨리 알게됩니다. 그러나 그러한 기능이있을 수 있고 문서를 확인하고 문서가 완전하고 이해 가능한 것이 아니라면 다른 기능의 존재를 어떻게 발견 할 수 있을까요? 나는 두 가지를 모두 호출하고 알고 있어야하는 두 가지 기능보다는 항상 의미있는 응답을 제공하는 하나의 기능을 원할 것입니다.


4
"답변 없음"및 "제로"응답을 동일한 반환 값으로 결합해야하는 이유는 무엇입니까? 대신에,이 방법은 "지난 5 년 동안의 이전 입원"을 반환하고 "이전 입원 목록이 이제까지 채워 졌습니까?"라는 별도의 방법을 갖습니다. 그것은 이전의 입원으로 채워지지 않은 목록과 채워지지 않은 목록 사이에 차이가 있다고 가정합니다.
John Saunders

2
그러나 null을 반환하면 이미 발신자에게 추가 부담이 있습니다! 호출자는 null에 대한 모든 반환 값을 확인해야합니다 (끔찍한 DRY 위반). "호출자가 질문에 대답하지 않았습니다"를 개별적 으로 표시하려면 사실을 표시하기 위해 추가 메소드 호출을 작성하는 것이 더 간단합니다. 또는 추가 속성이 DeclinedToAnswer 인 파생 컬렉션 형식을 사용하십시오. null을 반환하지 않는 규칙은 전혀 자의적인 것이 아니라 가장 작은 서프라이즈의 원칙입니다. 또한 메소드의 리턴 유형은 해당 이름의 기능을 의미해야합니다. 거의 확실하지 않습니다.
jammycakes

2
getHospitalizationList가 한 곳에서만 호출되고 /하거나 모든 발신자가 "응답 없음"과 "제로"사례를 구분하려고한다고 가정합니다. 호출자가 구별 할 필요가없는 경우 (거의 확실하게 대다수) 가 있을 수 있으므로, 이것이 필요하지 않은 곳에 널 검사를 추가하도록 강요하고 있습니다. 사람들이 너무 쉽게 잊어 버릴 수 있기 때문에 코드베이스에 심각한 위험을 초래합니다 . 컬렉션 대신 null을 반환 해야하는 합법적 인 이유가 거의 없기 때문에 훨씬 가능성이 높습니다.
jammycakes

3
RE 이름 : 함수 이름은 함수만큼 길지 않아서 비현실적이지 않으면 함수가 수행하는 작업을 완전히 설명 할 수 없습니다. 그러나 어떤 경우에도 응답이 없을 때 함수가 빈 목록을 반환하면 동일한 추론으로 "getHospitalizationListOrEmptyListIfNoAnswer"라고 부르지 않아야합니까? 그러나 실제로 Java Reader.read 함수의 이름을 readCharacterOrReturnMinusOneOnEndOfStream으로 바꾸어야한다고 주장합니까? ResultSet.getInt가 실제로 "getIntOrZeroIfValueWasNull"이어야합니까? 기타
Jay

4
RE는 모든 통화가 구별되기를 원합니다. 네, 적어도 발신자의 저자는 자신이 상관하지 않는 의식적인 결정을 내려야한다고 가정합니다. 함수가 "모름"에 대한 빈 목록을 반환하고 호출자가이 "없음"을 맹목적으로 처리하면 결과가 심각하게 부정확해질 수 있습니다. 함수가 "getAllergicReactionToMedicationList"인 경우를 상상해보십시오. "환자에게 알려진 알레르기 반응이 없다"라는 "맹목적으로 입력되지 않은"프로그램은 문자 그대로 paitent를 죽일 수 있습니다. 많은 다른 시스템에서 결과가 덜 극적인 경우 비슷한 결과를 얻을 수 있습니다. ...
Jay

10

빈 컬렉션이 의미 적으로 의미가 있다면 그것이 반환하는 것을 선호합니다. 빈 모음을 반환하면 GetMessagesInMyInbox()"받은 편지함에 실제로 메시지가 없습니다"라는 메시지가 표시되지만 반환 null하면 목록이 어떻게 표시되어야하는지 알 수없는 데이터가 충분하지 않다는 것을 알려주는 데 유용 할 수 있습니다.


6
당신이주는 예제에서, 단지 null을 반환하는 것보다 요청을 이행 할 수 없다면 메소드가 예외를 throw 해야하는 것처럼 들립니다. 예외는 널보다 문제점을 진단하는 데 훨씬 더 유용합니다.
Greg Beech

글쎄,받은 편지함 예제에서 null값이 반드시 합리적으로 보이지는 않습니다. 나는 그에 대해 더 일반적인 용어로 생각하고있었습니다. 예외는 무언가 잘못되었다는 사실을 알리는데도 유용하지만 언급 된 "불충분 한 데이터"가 완벽하게 예상되면 예외를 던지면 디자인이 나빠질 수 있습니다. 오히려 메서드가 때로는 응답을 계산할 수없는 완벽하게 가능하고 전혀 오류가없는 시나리오를 생각하고 있습니다.
David Hedlund

6

새로운 객체가 생성되지 않으므로 null을 반환하는 것이 더 효율적일 수 있습니다. 그러나 종종 null검사 (또는 예외 처리)가 필요합니다 .

의미 상, null 으로 빈 목록이 동일한 것을 의미하지는 않습니다. 차이점은 미묘하며 특정 사례에서 하나의 선택이 다른 선택보다 낫습니다.

선택에 관계없이 혼동을 피하기 위해 문서화하십시오.


8
API 디자인의 정확성을 고려할 때 효율성은 거의 영향을 미치지 않습니다. 그래픽 프리미티브와 같은 매우 구체적인 경우에는 그렇게 될 수 있지만 목록 및 대부분의 다른 고급 항목을 다룰 때 나는 그것을 의심합니다.
Greg Beech

특히이 "최적화"를 보상하기 위해 API 사용자가 작성해야하는 코드가 더 나은 디자인을 처음 사용했을 때보 다 비효율적 일 수 있다는 점에서 Greg와 동의하십시오.
Craig Stuntz

합의했으며 대부분의 경우 최적화 할 가치가 없습니다. 빈 메모리 목록은 현대적인 메모리 관리 기능으로 사실상 무료입니다.
Jason Baker


4

상황에 따라 다릅니다. 특별한 경우에는 null을 반환합니다. 함수가 빈 컬렉션을 반환하는 경우 분명히 반환하는 것이 좋습니다. 그러나 잘못된 매개 변수 또는 다른 이유로 인해 빈 컬렉션을 특별한 경우로 반환하는 것은 특별한 경우 조건을 가리기 때문에 좋은 생각이 아닙니다.

실제로,이 경우 일반적으로 예외를 무시하여 실제로 무시되지 않는지 확인하는 것이 좋습니다. :)

null 조건을 처리 할 필요가 없기 때문에 빈 컬렉션을 반환하여 코드를보다 견고하게 만든다는 것은 호출 코드로 처리 해야하는 문제를 가리기 때문에 좋지 않습니다.


4

나는 그것이 null빈 컬렉션과 같은 것이 아니라고 주장하며 반환하는 것을 가장 잘 나타내는 것을 선택해야합니다. 대부분의 경우 null아무것도 아닙니다 (SQL 제외). 빈 컬렉션은 비어 있지만 무언가입니다.

둘 중 하나를 선택 해야하는 경우 null이 아닌 빈 컬렉션으로 향해야한다고 말하고 싶습니다. 그러나 빈 컬렉션이 null 값과 다른 경우가 있습니다.


4

고객을 선호한다고 생각하십시오 (귀하의 API를 사용하는).

'null'을 반환하면 클라이언트가 null 검사를 올바르게 처리하지 못하는 문제가 종종 발생하여 런타임 중에 NullPointerException이 발생합니다. 누락 된 null 검사가 우선 순위 생산 문제 (null 값에 foreach (...)를 사용하는 클라이언트)를 강요 한 사례를 보았습니다. 테스트하는 동안 조작 된 데이터가 약간 다르기 때문에 문제점이 발생하지 않았습니다.


3

적절한 예를 들어 여기에 설명하고 싶습니다.

여기에서 사례를 고려하십시오 ..

int totalValue = MySession.ListCustomerAccounts()
                          .FindAll(ac => ac.AccountHead.AccountHeadID 
                                         == accountHead.AccountHeadID)
                          .Sum(account => account.AccountValue);

여기에 내가 사용하는 기능을 고려하십시오 ..

1. ListCustomerAccounts() // User Defined
2. FindAll()              // Pre-defined Library Function

난 쉽게 사용할 수 ListCustomerAccountFindAll대신의.,

int totalValue = 0; 
List<CustomerAccounts> custAccounts = ListCustomerAccounts();
if(custAccounts !=null ){
  List<CustomerAccounts> custAccountsFiltered = 
        custAccounts.FindAll(ac => ac.AccountHead.AccountHeadID 
                                   == accountHead.AccountHeadID );
   if(custAccountsFiltered != null)
      totalValue = custAccountsFiltered.Sum(account => 
                                            account.AccountValue).ToString();
}

참고 : AccountValue가 아니기 때문에 nullSum () 함수는 null.을 반환하지 않으므로 직접 사용할 수 있습니다.


2

우리는 일주일 정도 전에 직장에서 개발 팀들 사이에서이 토론을했으며 거의 ​​만장일치로 빈 수거를갔습니다. 한 사람이 Mike가 위에서 지정한 것과 같은 이유로 null을 반환하려고했습니다.


2

빈 컬렉션. C #을 사용하는 경우 시스템 리소스를 최대화 할 필요가 없다고 가정합니다. 비효율적이지만, 빈 컬렉션을 반환하는 것은 관련된 프로그래머에게 훨씬 편리합니다 (위에 설명 된 이유로).


2

대부분의 경우 빈 컬렉션을 반환하는 것이 좋습니다.

그 이유는 호출자의 구현 편의성, 일관된 계약 및 쉬운 구현 때문입니다.

빈 결과를 나타 내기 위해 메서드가 null을 반환하면 호출자는 열거 외에도 null 확인 어댑터를 구현해야합니다. 그런 다음이 코드는 다양한 호출자에서 복제되므로 재사용 할 수 있도록 메소드 내에이 어댑터를 넣지 마십시오.

IEnumerable에 유효한 null 사용은 결과가 없거나 작업 실패를 나타내는 것일 수 있지만이 경우 예외 발생과 같은 다른 기술을 고려해야합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace StackOverflow.EmptyCollectionUsageTests.Tests
{
    /// <summary>
    /// Demonstrates different approaches for empty collection results.
    /// </summary>
    class Container
    {
        /// <summary>
        /// Elements list.
        /// Not initialized to an empty collection here for the purpose of demonstration of usage along with <see cref="Populate"/> method.
        /// </summary>
        private List<Element> elements;

        /// <summary>
        /// Gets elements if any
        /// </summary>
        /// <returns>Returns elements or empty collection.</returns>
        public IEnumerable<Element> GetElements()
        {
            return elements ?? Enumerable.Empty<Element>();
        }

        /// <summary>
        /// Initializes the container with some results, if any.
        /// </summary>
        public void Populate()
        {
            elements = new List<Element>();
        }

        /// <summary>
        /// Gets elements. Throws <see cref="InvalidOperationException"/> if not populated.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>.</returns>
        public IEnumerable<Element> GetElementsStrict()
        {
            if (elements == null)
            {
                throw new InvalidOperationException("You must call Populate before calling this method.");
            }

            return elements;
        }

        /// <summary>
        /// Gets elements, empty collection or nothing.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with zero or more elements, or null in some cases.</returns>
        public IEnumerable<Element> GetElementsInconvenientCareless()
        {
            return elements;
        }

        /// <summary>
        /// Gets elements or nothing.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with elements, or null in case of empty collection.</returns>
        /// <remarks>We are lucky that elements is a List, otherwise enumeration would be needed.</remarks>
        public IEnumerable<Element> GetElementsInconvenientCarefull()
        {
            if (elements == null || elements.Count == 0)
            {
                return null;
            }
            return elements;
        }
    }

    class Element
    {
    }

    /// <summary>
    /// http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection/
    /// </summary>
    class EmptyCollectionTests
    {
        private Container container;

        [SetUp]
        public void SetUp()
        {
            container = new Container();
        }

        /// <summary>
        /// Forgiving contract - caller does not have to implement null check in addition to enumeration.
        /// </summary>
        [Test]
        public void UseGetElements()
        {
            Assert.AreEqual(0, container.GetElements().Count());
        }

        /// <summary>
        /// Forget to <see cref="Container.Populate"/> and use strict method.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void WrongUseOfStrictContract()
        {
            container.GetElementsStrict().Count();
        }

        /// <summary>
        /// Call <see cref="Container.Populate"/> and use strict method.
        /// </summary>
        [Test]
        public void CorrectUsaOfStrictContract()
        {
            container.Populate();
            Assert.AreEqual(0, container.GetElementsStrict().Count());
        }

        /// <summary>
        /// Inconvenient contract - needs a local variable.
        /// </summary>
        [Test]
        public void CarefulUseOfCarelessMethod()
        {
            var elements = container.GetElementsInconvenientCareless();
            Assert.AreEqual(0, elements == null ? 0 : elements.Count());
        }

        /// <summary>
        /// Inconvenient contract - duplicate call in order to use in context of an single expression.
        /// </summary>
        [Test]
        public void LameCarefulUseOfCarelessMethod()
        {
            Assert.AreEqual(0, container.GetElementsInconvenientCareless() == null ? 0 : container.GetElementsInconvenientCareless().Count());
        }

        [Test]
        public void LuckyCarelessUseOfCarelessMethod()
        {
            // INIT
            var praySomeoneCalledPopulateBefore = (Action)(()=>container.Populate());
            praySomeoneCalledPopulateBefore();

            // ACT //ASSERT
            Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
        }

        /// <summary>
        /// Excercise <see cref="ArgumentNullException"/> because of null passed to <see cref="Enumerable.Count{TSource}(System.Collections.Generic.IEnumerable{TSource})"/>
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void UnfortunateCarelessUseOfCarelessMethod()
        {
            Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
        }

        /// <summary>
        /// Demonstrates the client code flow relying on returning null for empty collection.
        /// Exception is due to <see cref="Enumerable.First{TSource}(System.Collections.Generic.IEnumerable{TSource})"/> on an empty collection.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void UnfortunateEducatedUseOfCarelessMethod()
        {
            container.Populate();
            var elements = container.GetElementsInconvenientCareless();
            if (elements == null)
            {
                Assert.Inconclusive();
            }
            Assert.IsNotNull(elements.First());
        }

        /// <summary>
        /// Demonstrates the client code is bloated a bit, to compensate for implementation 'cleverness'.
        /// We can throw away the nullness result, because we don't know if the operation succeeded or not anyway.
        /// We are unfortunate to create a new instance of an empty collection.
        /// We might have already had one inside the implementation,
        /// but it have been discarded then in an effort to return null for empty collection.
        /// </summary>
        [Test]
        public void EducatedUseOfCarefullMethod()
        {
            Assert.AreEqual(0, (container.GetElementsInconvenientCarefull() ?? Enumerable.Empty<Element>()).Count());
        }
    }
}

2

저는 이것을 10 억 달러의 실수라고 부릅니다. 그 당시에는 객체 지향 언어로 참조 할 수있는 최초의 포괄적 인 유형 시스템을 설계했습니다. 필자의 목표는 컴파일러가 자동으로 검사를 수행하여 모든 참조 사용이 절대적으로 안전하도록하는 것이 었습니다. 그러나 구현하기가 쉽기 때문에 null 참조를 넣는 유혹에 저항 할 수 없었습니다. 이로 인해 수많은 오류, 취약성 및 시스템 충돌이 발생했으며, 이는 아마도 지난 40 년 동안 수십억 달러의 고통과 피해를 야기했을 것입니다. – Tony Hoare, ALGOL W.의 발명가

일반적으로 정교한 똥 폭풍에 대해서는 여기 를 참조 하십시오null . 나는 undefined다른 진술에 동의하지 않지만 null여전히 읽을 가치가 있습니다. 그리고 그것은 null당신이 요구 한 경우가 아니라 왜 피해야 하는지를 설명합니다 . 본질은 null모든 언어에서 특별한 경우입니다. null예외 로 생각해야합니다 . undefined정의되지 않은 동작을 다루는 코드는 대부분 버그 일뿐입니다. C와 대부분의 다른 언어에도 정의되지 않은 동작이 있지만 대부분 언어의 식별자는 없습니다.


1

주요 소프트웨어 엔지니어링 목표 인 복잡성 관리 측면에서 불필요한 클라이언트 순환 복잡성을 API 클라이언트에 전파 하지 않기를 원합니다 . 클라이언트에 null을 반환하는 것은 다른 코드 분기의 순환 복잡성 비용을 반환하는 것과 같습니다.

(이는 단위 테스트 부담에 해당합니다. 빈 콜렉션 리턴 케이스 외에 널 리턴 케이스에 대한 테스트를 작성해야합니다.)

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