369

그것은 일종의 일반적인 질문입니다. (하지만 C #을 사용하고 있습니다.) 가장 좋은 방법은 무엇입니까 (모범 사례), 콜렉션이 반환 유형으로 포함 된 메소드에 대해 null 또는 빈 콜렉션을 반환합니까?


  • 음, 꽤 CPerkins 아닙니다.rads.stackoverflow.com/amzn/click/0321545613 - Will
  • @CPerkins - 그렇습니다. Microsoft의 .NET 프레임 워크 디자인 지침에 명시되어 있습니다. 자세한 내용은 RichardOD의 답변을 참조하십시오. - Greg Beech
  • 의미가 " 결과를 계산할 수 없습니다 " null을 반환해야합니다. 널 (Null)은 결코 "비어 있음", "누락" 또는 "알 수 없음"을 포함한다. 주제에 대한 내 기사의 자세한 내용 :blogs.msdn.com/ericlippert/archive/2009/05/14/… - Eric Lippert
  • Bozho : "임의의" 엄청난 양의 언어입니다. 빈리스트가 정확히 null 값과 같은 Common Lisp은 어떻습니까? :-) - Ken
  • 실제로, "복제" 컬렉션이 아니라 객체를 반환하는 메서드에 대한 것입니다. 답변이 다른 시나리오가 다릅니다. - GalacticCowboy

18 답변


443

빈 컬렉션입니다. 항상.

이거 끔찍해.

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>()예를 들어 새로운 빈 컬렉션 또는 배열과 같이 반환하는 것보다 더 효율적으로 볼 수 있습니다.


  • 나는 Will과 동의하지만, 나는 항상 "always"라고 생각한다. 조금 과다. 빈 콜렉션이 "0 아이템"을 의미하는 동안, 널을 리턴하는 것은 "전혀 콜렉션 없음"을 의미 할 수있다. - 예. HTML을 파싱하는 경우 < ul > id = "foo", < ul id = "foo"> < / ul > 빈 콜렉션을 반환 할 수있다. < ul > id = "foo" 널 리턴이 더 좋을 것입니다 (예외로이 케이스를 처리하지 않으려는 경우) - Patonza
  • " 빈 배열을 쉽게 반환 할 수 있는지 여부>가 아닌 현재 배열에서 빈 배열이 오해의 소지가 있는지 여부에 대한 질문이 항상 있습니다. 빈 배열은 사실 null을 의미합니다. 널이 아닌 빈 배열을 항상 반환해야한다고 말하는 것은 부울 메서드가 항상 true를 반환해야한다고 말하는 것처럼 잘못된 것입니다. 두 값 모두 의미를 전달합니다. - David Hedlund
  • 실제로 새로운 Foo [0] 대신 System.Linq.Enumerable.Empty < Foo > ()를 반환하는 것이 좋습니다. 그것은 더 명시 적이며 적어도 하나의 메모리 할당을 저장합니다 (적어도 설치된 .NET 구현에서). - Trillian
  • @ 윌 : OP : " 당신은 null 또는 빈 컬렉션을 반환합니까?방법반환 유형으로 컬렉션이 있습니다. " 나는이 경우에 그것이    IEnumerable또는ICollection그다지 중요하지 않습니다. 어쨌든, 당신이 뭔가를 선택하면ICollection그들은 또한 돌아온다.null... 나는 그들에게 빈 컬렉션을 돌려 주길 원하지만, 나는 그들을 돌려 보내고null그래서 나는 여기서 그걸 언급 할 것이라고 생각했습니다. 열거 형 컬렉션의 기본값이 null이 아니라고 말할 것입니다. 민감한 주제라는 것을 알지 못했습니다. - Matthijs Wessels
  • 관련 (CodeProject 기사) : null 대신 빈 목록을 반환하는 것이 정말 낫습니다. ' - Krumia

147

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

에서 Null 값을 반환하지 마십시오.   컬렉션 속성 또는 메서드에서   컬렉션을 반환합니다. 빈을 반환하십시오.   컬렉션 또는 빈 배열을 대신 사용하십시오.

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

편집하다-에릭 리 퍼트 (Eric Lippert)가 원래 질문에 댓글을 달았 기 때문에그의 뛰어난 기사에 대한 링크.


  • +1. 매우 좋은 이유가 없다면 프레임 워크 설계 지침을 따르는 것이 항상 최선의 방법입니다. - Will
  • @Will, 절대적으로. 그게 제가 따르는 것입니다. 별도로해야 할 필요성을 발견하지 못했습니다. - RichardOD
  • 예, 그게 당신이 따르는 것입니다. 하지만 API를 따르지 않아도 의존하지 않는 API의 경우에는       ;) - Bozho
  • @ Bozho yeap. 당신의 대답은 그러한 프린지 경우에 대한 좋은 대답을 제공합니다. - RichardOD

84

너의에 달렸어.계약그리고 당신의구체적인 경우. 일반적으로빈 콜렉션을 반환하는 것이 가장 좋습니다., 하지만 가끔은 (드물게) :

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

몇 가지 구체적인 예 :

  • UI 구성 요소 (컨트롤 외부의 라이브러리에서 가져온 것)는 빈 컬렉션이 전달되면 빈 테이블을 렌더링하거나 null이 전달되면 테이블을 전혀 렌더링하지 않을 수 있습니다.
  • Object-to-XML (JSON / 뭐든간에)에서null요소가없는 것을 의미하고, 빈 콜렉션은 중복 된 (그리고 아마도 부정확 한)<collection />
  • null을 반환 / 전달해야한다는 명시 적으로 API를 사용하거나 구현하고 있습니다.


  • @ Bozho- 몇 가지 흥미로운 예가 있습니다. - RichardOD
  • 다른 결함 및 정의와 관련하여 코드를 작성해야한다고 생각하지 않지만 포인트 이벤트를 좀 볼 수 있습니다.     C #구체적인 것을 의미합니다. 값 null은 "no information"로 정의된다. 그러므로 특정 정보를 가지고 있다고 말하는 것은 옥시몬 (oximoron)이다. .NET 지침에서는 집합이 실제로 비어 있으면 빈 집합을 반환해야한다고 설명했습니다. " 예상되는 세트가 어느 위치에 도착했는지 알 수 없음 " - Rune FS
  • 아니요, "설정이 없습니다"라는 의미입니다. "세트에는 요소가 없다" - Bozho
  • 나는 그때, 나는 TSQL을 많이 써야만하고 항상 그런 것은 아니라고 믿었었다. - Will
  • Object-To-Xml이있는 부분은 다음 위치에 있습니다. - Mihai Caracostea

34

아직 언급되지 않은 한 가지 다른 점이 있습니다. 다음 코드를 살펴보십시오.

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

C #언어는이 메서드를 호출 할 때 빈 열거자를 반환합니다. 따라서 언어 디자인 (따라서 프로그래머의 기대치)과 일치하려면 빈 콜렉션을 리턴해야합니다.


  • 좋아하는 emo 노래가 비어있는 경우 +1. - Jesse C. Slicer
  • +1. 제프리. 좋은 예. - RichardOD
  • 이 예제는 나를 슬프게 만든다. - Ken
  • 기술적으로 이것이 빈 컬렉션을 반환한다고 생각하지 않습니다. - FryGuy
  • @FryGuy - 아니야. Enumerable 개체를 반환하며 GetEnumerator () 메서드는 콜렉션과 함께 analagy가 비어있는 Enumerator를 반환합니다. 즉, Enumerator의 MoveNext () 메서드는 항상 false를 반환합니다. 예제 메서드를 호출해도 null이 반환되지 않으며 GetEnumerator () 메서드가 null을 반환하는 Enumerable 객체도 반환하지 않습니다. - Jeffrey L Whitledge

29

비어있는 것은 소비자 친화적입니다.

빈 열거 형을 만드는 명확한 방법이 있습니다.

Enumerable.Empty<Element>()


  • 깔끔한 트릭을 가져 주셔서 감사합니다. 나는 바보처럼 빈 List ()를 인스턴스화하고 있었지만 이것은 훨씬 더 깨끗해 보였으며 아마도 조금 더 효율적이기도하다. - Repo Man

18

상황에 따라 의미 상으로 올바른 값을 반환해야합니다. "항상 빈 컬렉션을 반환합니다"라는 규칙은 나에게 조금 단순 해 보입니다.

예를 들어 한 병원의 시스템에서 지난 5 년간 모든 이전 입원 목록을 반환해야한다고 가정합니다. 고객이 병원에 없었다면 빈 목록을 반환하는 것이 좋습니다. 하지만 고객이 입학 양식의 해당 부분을 비워두면 어떻게 될까요? "빈 목록"과 "무응답"또는 "알지 못함"을 구별하기 위해 다른 값이 필요합니다. 우리는 예외를 던질 수는 있지만 오류 조건 일 필요는 없으며 정상적인 프로그램 흐름에서 우리를 몰아 내지는 않습니다.

나는 종종 0과 무응답을 구별 할 수없는 시스템에 좌절감을 나타냈다. 나는 시스템이 어떤 숫자를 입력하라고 요구 한 횟수가 있었는데, 0을 입력하고,이 필드에 값을 입력해야한다는 오류 메시지가 나타납니다. 방금 했어. 나는 0으로 들어갔다. 그러나 아무 대답도 구별 할 수 없으므로 0을받지 않습니다.


손더스에게 대답하십시오.

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

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

내가 볼 수있는 유일한 이점은 당신이 임의의 규칙을 준수 할 수 있다는 것입니다. 이 규칙에 복종 할만한 가치가있는 이점이 있습니까? 그렇지 않다면, 왜 귀찮게합니까?


Jammycakes에 회신 :

실제 코드가 어떻게 보이는지 생각해보십시오. 나는 질문을 알고 C #을하지만 자바를 쓰면 실례합니다. 내 C #은 그다지 날카롭지 않고 원리도 같습니다.

null 반환 :

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 ...
}

실제로는 null return이있는 줄 또는 코드가 두 줄이므로 발신자에게 부담이되지 않습니다.

DRY 문제가 어떻게 발생하는지는 알 수 없습니다. 우리가 두 번 전화를해야하는 것과는 다릅니다. 리스트가 존재하지 않을 때 우리가 항상 똑같은 일을하고 싶다면 호출자가 호출하는 대신 get-list 함수로 처리를 푸시 할 수 있으므로 호출자에 코드를 넣는 것은 DRY 위반이됩니다. 그러나 우리는 항상 같은 일을 항상하고 싶지 않습니다. 처리 할 목록이 있어야하는 함수에서 누락 된 목록은 처리를 중단시킬 수있는 오류입니다. 그러나 편집 화면에서 아직 데이터를 입력하지 않은 경우 처리를 중단하고 싶지는 않습니다. 데이터를 입력 할 수있게하려는 것입니다. 따라서 "no list"처리는 호출자 수준에서 수행되어야합니다. 그리고 우리가 null 반환 또는 별도의 기능으로 그렇게하든 더 큰 원칙에 아무런 차이가 없습니다.

물론 호출자가 null을 확인하지 않으면 프로그램이 널 포인터 예외로 실패 할 수 있습니다. 그러나 별도의 "got"함수가 있고 호출자가 해당 함수를 호출하지 않고 맹목적으로 "get list"함수를 호출하면 어떤 일이 발생합니까? 예외를 던지거나 그렇지 않으면 실패 할 경우에는 null을 반환하고 확인하지 않은 경우와 거의 같습니다. 비어있는 목록을 반환하는 경우에는 잘못된 것입니다. 당신은 "나는 원소가없는 목록이있다"와 "나는 목록이 없다"를 구별하지 못하고있다. 사용자가 가격을 입력하지 않은 경우 가격을 0으로 반환하는 것과 같습니다. 단지 잘못되었습니다.

컬렉션에 추가 속성을 첨부하는 것이 어떻게 도움이되는지 나는 알지 못합니다. 발신자는 여전히 그것을 확인해야합니다. null을 검사하는 것보다 그게 낫지? 다시 한 번 말하지만, 프로그래머가이를 확인하고 잘못된 결과를주는 것을 잊어 버리는 것이 가장 최악의 경우입니다.

프로그래머가 "가치가 없다"는 의미의 null 개념에 익숙하다면 null을 리턴하는 함수는 놀랄만 한 일이 아닙니다. 유능한 프로그래머라면 누구나 들어 보았을 것입니다. 좋은 생각인지 아닌지 생각합니다. 나는 분리 된 기능을 갖는 것이 "놀라움"문제에 더 가깝다고 생각한다. 프로그래머가 API에 익숙하지 않은 경우 데이터가없는 테스트를 실행하면 때때로 null이 반환된다는 것을 신속하게 발견하게됩니다. 그러나 그와 같은 기능이있을 수 있고 문서를 검사하지 않으면 문서가 완전하고 이해할 수있는 것이 아니라면 어떻게 다른 기능의 존재를 발견 할 수 있을까요? 나는 오히려 의미있는 응답을 제공하는 하나의 기능을 갖기보다는 오히려 두 가지 기능 모두를 호출해야한다는 것을 기억해야합니다.


  • "무응답"을 결합하는 것이 왜 필요한가? 및 "제로" 동일한 반환 값의 응답? 대신,이 방법으로 " 지난 5 년 동안의 이전 입원 "을 반환하고 "이전 입원 목록이 채워 졌습니까?"라고 묻는 별도의 방법을 사용하십시오. 이는 이전에 입원 한 적이없는 목록과 채워지지 않은 목록간에 차이가 있다고 가정합니다. - John Saunders
  • 하지만 null을 반환하면 이미 호출자에게 부담을주고 있습니다! 호출자는 null에 대한 모든 반환 값을 확인해야합니다. 이는 끔찍한 DRY 위반입니다. " 발신자가 질문에 답변하지 않았습니다 "라고 표시하려면 별도로~이다.사실을 나타 내기 위해 추가 메서드 호출을 만드는 것이 더 간단합니다. 그 중 하나 또는 DeclinedToAnswer의 추가 속성이있는 파생 된 컬렉션 형식을 사용하십시오. null을 반환하지 않는 규칙은 전혀 임의적이지 않습니다. 최소한의 놀라움의 원리입니다. 또한 메소드의 반환 유형은 이름의 의미를 의미해야합니다. Null은 거의 확실하지 않습니다. - jammycakes
  • getHospitalizationList가 한 곳에서만 호출되고 / 또는 모든 발신자가 " 무응답 "을 구별하기를 원한다고 가정합니다. 및 "제로" 사례. 그곳에의지발신자가 구별 할 필요가없는 경우 (대부분 대다수)가 될 수 있으므로 필요하지 않아야하는 장소에 널 수표를 추가해야합니다. 이것은 사람들이 모두 그렇게 쉽게 잊을 수 있기 때문에 코드베이스에 상당한 위험을 더합니다.본격적인컬렉션 대신 null을 반환하는 이유는 훨씬 더 가능성이 높습니다. - jammycakes
  • RE 이름 : 함수 이름은 함수가 길지 않은 한 함수가 무엇을하는지 완벽하게 설명 할 수 없으므로 비현실적입니다. 그러나 어떤 경우에도 함수가 응답이 없을 때 함수가 빈 목록을 반환하면 동일한 추론을 통해 " getHospitalizationListOrEmptyListIfNoAnswer "라는 호출을하지 않아야합니다. 하지만 실제로 Java Reader.read 함수의 이름을 readCharacterOrReturnMinusOneOnEndOfStream으로 변경해야한다고 주장 하시겠습니까? 그 ResultSet.getInt는 실제로는 " getIntOrZeroIfValueWasNull " 기타. - Jay
  • 모든 전화는 구별하기를 원합니다. 음, 그렇습니다. 적어도 호출자의 저자는 자신이 신경 쓰지 않는다는 의식적 결정을 내려야한다고 생각합니다. 함수가 "모름"에 대한 빈리스트를 반환하고 발신자가 맹목적으로이 "없음"을 처리하면, 심각하게 부정확 한 결과를 줄 수있다. 함수가 " getAllergicReactionToMedicationList " 였는지 상상해보십시오. 맹목적으로 "list was not entered"를 처리 한 프로그램 "환자는 알려진 알레르기 반응을 나타내지 않는다" 말 그대로 유행을 죽일 수 있습니다. 다른 많은 시스템에서는 비슷한 결과를 얻지 만 그다지 놀라운 결과는 아닙니다. ... - Jay

10

의미없는 의미에서 빈 콜렉션이 의미를 갖게된다면, 나는 그것을 반환하는 것을 선호한다. 에 대한 빈 콜렉션 리턴GetMessagesInMyInbox()"받은 편지함에 실제로 메시지가 없습니다"라는 메시지를 전달하고null반환해야 할 목록이 어떤 모양인지 말해야하는 불충분 한 데이터가 있음을 알리는 데 유용 할 수 있습니다.


  • 이 예제에서 null을 반환하는 것보다는 요청을 수행 할 수있는 경우 메서드에서 예외를 throw해야하는 것처럼 들립니다. 예외는 널 (null)보다 문제점 진단에 더 유용합니다. - Greg Beech
  • 예,받은 편지함 예제에서null가치는 분명히 합리적인 것으로 보이지 않습니다. 나는 그것에 대해 좀 더 일반적인 용어로 생각하고있었습니다. 예외는 무언가가 잘못되었다는 사실을 알리는 데에도 효과적이지만, "불충분 한 데이터" 언급 된 것이 완벽하게 예상되는 경우 예외가 발생하면 잘못된 디자인이됩니다. 오히려 완벽하게 가능하고 어떤 방법으로도 응답을 계산할 수없는 오류가없는 시나리오를 생각해보십시오. - David Hedlund

6

새로운 객체가 생성되지 않기 때문에 null을 반환하는 것이 더 효율적일 수 있습니다. 그러나 또한 종종null확인 (또는 예외 처리).

의미 상으로,null빈 목록은 같은 것을 의미하지는 않습니다. 차이점은 미묘하며 특정한 경우에는 하나의 선택이 다른 것보다 좋을 수 있습니다.

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


  • API 디자인의 정확성을 고려할 때 효율성이 거의 영향을 미치지 않아야합니다. 그래픽 프리미티브와 같은 매우 특정한 경우에는 그렇게 될 수도 있지만, 목록과 다른 고수준의 것들을 다룰 때는 매우 의심 스럽습니다. - Greg Beech
  • Greg와 동의하십시오. 특히 API 사용자가이 '최적화'기능을 보완하기 위해 작성해야하는 코드가 있다면 처음에는 더 나은 디자인이 사용 된 경우보다 비효율적 일 수 있습니다. - Craig Stuntz
  • 동의하고 대부분의 경우 단순히 최적화할만한 가치가 없습니다. 빈 목록은 현대적인 메모리 관리로는 사실상 무료입니다. - Jason Baker

6

그 이유에 대한 논쟁은Null 객체 패턴빈 콜렉션을 리턴하는 것을 선호하는 것과 비슷합니다.


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가 아니기 때문에null, Sum () 함수는  반환null그래서, 나는 그것을 직접 사용할 수있다.


2

우리는 일주일 정도 전에 일하는 개발팀들 사이에서이 토론을 가졌으며, 거의 만장일치로 빈 컬렉션에 나섰습니다. 한 명은 위에 지정된 Mike와 같은 이유로 null을 반환하려고했습니다.


2

빈 컬렉션입니다. C #을 사용한다면, 시스템 자원을 최대화하는 것이 필수적인 것은 아니라는 가정이 있습니다. 효율성이 떨어지긴하지만 Empty Collection을 반환하는 것은 관련 프로그래머에게 훨씬 편리합니다 (위에 설명 된 이유 때문에).


2

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

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

메소드가 빈 결과를 나타 내기 위해 null을 리턴하면 호출자는 열거 이외에 널 (NULL) 점검 어댑터를 구현해야합니다. 이 코드는 다양한 호출자에서 중복되므로 왜이 어댑터를 메소드 내에 두어 재사용 할 수 있는지 확인하십시오.

IEnumerable에 대해 유효한 null을 사용하면 결과가 없음 또는 작업이 실패했음을 알 수 있지만이 경우 예외를 throw하는 등의 다른 기술을 고려해야합니다.

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 억 달러의 실수라고 부릅니다. 당시 저는 객체 지향 언어로 된 참조를위한 최초의 포괄적 인 유형 시스템을 설계하고있었습니다. 필자의 목표는 컴파일러가 자동으로 수행하는 검사와 함께 참조의 모든 사용이 절대적으로 안전해야한다는 것입니다. 그러나 구현하기가 쉽지 않기 때문에 널 참조에 넣으려는 유혹에 저항 할 수 없었습니다. 이로 인해 지난 40 년간 수십억 달러의 고통과 피해를 초래할 수있는 수많은 오류, 취약성 및 시스템 고장이 발생했습니다.   -ALGOL W.의 발명가 인 Tony Hoare

만나다이리정교한 똥의 폭풍에 대해null일반적으로 나는 그 진술에 동의하지 않는다.undefined또 다른null, 그러나 그것은 여전히 읽을만한 가치가 있습니다. 그리고 그것이 왜 당신이 피해야하는지 설명합니다.null당신이 요청한 경우뿐만 아니라 전혀. 본질은null어떤 언어로 된 특별한 경우입니다. 너는 생각해야 해.null예외로undefined그렇게 정의되지 않은 행동을 다루는 코드는 대부분 버그에 불과합니다. C와 다른 대부분의 언어는 정의되지 않은 동작을하지만 대부분 언어의 식별자는 없습니다.


1

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

(이것은 단위 테스트 부담에 해당하며 빈 콜렉션 반환 케이스 외에도 null 반환 케이스에 대한 테스트를 작성해야합니다.)

연결된 질문


최근 질문