Das ist eine Art eine allgemeine Frage (aber ich bin mit C#), was ist der beste Weg (Best Practice), geben Sie null oder leere Sammlung für eine Methode, die eine Sammlung als Rückgabetyp hat?
Antworten
Zu viele Anzeigen?Ich möchte das hier anhand eines passenden Beispiels erläutern.
Betrachten Sie hier einen Fall.
int totalValue = MySession.ListCustomerAccounts()
.FindAll(ac => ac.AccountHead.AccountHeadID
== accountHead.AccountHeadID)
.Sum(account => account.AccountValue);
Betrachten Sie hier die Funktionen, die ich verwende
1. ListCustomerAccounts() // User Defined
2. FindAll() // Pre-defined Library Function
Ich kann leicht verwenden ListCustomerAccount
y FindAll
statt.,
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();
}
HINWEIS: Da AccountValue nicht null
wird die Funktion Summe() nicht zurückgeben. null
Daher kann ich es direkt verwenden.
Die Rückgabe einer leeren Sammlung ist in den meisten Fällen besser.
Der Grund dafür ist die Bequemlichkeit der Implementierung des Aufrufers, ein konsistenter Vertrag und eine einfachere Implementierung.
Wenn eine Methode null zurückgibt, um ein leeres Ergebnis anzuzeigen, muss der Aufrufer zusätzlich zur Aufzählung einen Adapter zur Nullprüfung implementieren. Dieser Code wird dann in verschiedenen Aufrufern dupliziert. Warum also nicht diesen Adapter in die Methode einbauen, damit er wiederverwendet werden kann?
Eine gültige Verwendung von null für IEnumerable könnte ein Hinweis auf ein fehlendes Ergebnis oder einen fehlgeschlagenen Vorgang sein, aber in diesem Fall sollten andere Techniken in Betracht gezogen werden, wie das Auslösen einer Ausnahme.
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());
}
}
}
Go scheint die einzige Sprache zu sein, in der nil
wird einem leeren Array vorgezogen.
https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices
Wenn Sie ein leeres Slice deklarieren, bevorzugen Sie
var t []string
übert := []string{}
. Der erste erklärt einenil
Slice-Wert, während der letztere nichtnil
aber Null-Länge. Sie sind funktionell gleichwertig - ihrelen
ycap
sind beide Null, aber dienil
Der bevorzugte Stil ist die Scheibe.
- See previous answers
- Weitere Antworten anzeigen
0 Stimmen
Dies ist eine subjektive Frage, bei der es auf beiden Seiten leidenschaftliche Gläubige gibt. Es gibt keine allgemein anerkannte "beste Praxis".
5 Stimmen
Ähm, nicht ganz CPerkins. rads.stackoverflow.com/amzn/click/0321545613
1 Stimmen
Nun, es gibt eine bewährte Praxis, von der es aber auch begründete Ausnahmen gibt.
3 Stimmen
@CPerkins - Ja, die gibt es. Es ist klar in Microsofts eigenen .NET-Framework-Entwurfsrichtlinien festgelegt. Siehe RichardOD's Antwort für die Details.
0 Stimmen
Die Sprache, um die es hier geht, ist zwar C#, aber alle diese Leitlinien gelten für jede objektorientierte Sprache, die es gibt.
55 Stimmen
NUR wenn die Bedeutung "Ich kann die Ergebnisse nicht berechnen" ist, sollten Sie null zurückgeben. Null sollte nie die Semantik von "leer" haben, sondern nur "fehlend" oder "unbekannt". Weitere Einzelheiten finden Sie in meinem Artikel zu diesem Thema: blogs.msdn.com/ericlippert/archive/2009/05/14/
3 Stimmen
Bozho: "Jede" ist eine ganze Menge von Sprachen. Wie wäre es mit Common Lisp, wo die leere Liste genau gleich dem Nullwert ist :-)
1 Stimmen
Duplikat: stackoverflow.com/questions/1626597/
9 Stimmen
Eigentlich geht es bei dem "Duplikat" um Methoden, die ein Objekt zurückgeben, nicht um eine Sammlung. Es ist ein anderes Szenario mit anderen Antworten.
0 Stimmen
@EricLippert der Link scheint jetzt auf eine Blog-Verzeichnis-Seite umzuleiten. Kannst du den Link aktualisieren? Danke!
0 Stimmen
@TheMuffinMan: Sie können alte Kommentare leider nicht bearbeiten. Microsoft verschiebt ständig den Ort, an dem sich mein alter Blog befindet. Der neue Link ist learn.microsoft.com/de-us/archive/blogs/ericlippert/
0 Stimmen
Ich hatte diese Frage auch, aber ich denke, null ist nicht leer Sammlung und es kann Problem in einigen Codes verursachen.