Sind Methoden, die den Referenztypparametern ändern?
-
05-07-2019 - |
Frage
Ich habe solche Methoden gesehen:
public void Foo(List<string> list)
{
list.Add("Bar");
}
Ist diese gute Praxis, Parameter in einer Methode zu ändern?
Wäre das nicht besser?
public List<string> Foo(List<string> list)
{
// Edit
List<string> newlist = new List<string>(list);
newlist.Add("Bar");
return newlist;
}
Es fühlt sich einfach so an, als hätte das erste Beispiel unerwartete Nebenwirkungen.
Lösung
In dem Beispiel, das Sie gegeben haben, scheint mir das erste viel schöner zu sein als der zweite. Wenn ich eine Methode sah, die eine Liste akzeptierte und auch eine Liste zurückgab, wäre meine erste Annahme, dass sie eine neue Liste zurückgab und nicht die, die sie erhielten. Die zweite Methode ist daher die mit unerwarteten Nebenwirkungen.
Solange Ihre Methoden angemessen genannt werden, besteht bei der Änderung des Parameters wenig Gefahr. Bedenken Sie:
public void Fill<T>(IList<T> list)
{
// add a bunch of items to list
}
Mit einem Namen wie "FILL" können Sie ziemlich sicher sein, dass die Methode die Liste ändert.
Andere Tipps
Ehrlich gesagt, in diesem Fall tun beide Methoden mehr oder weniger dasselbe. Beide ändern die List
das wurde eingegangen.
Wenn das Ziel nach einer solchen Methode unababstimmung werden soll, sollte das zweite Beispiel eine Kopie des List
das wurde eingesandt und dann die durchführen Add
Betrieb auf dem neuen List
und dann zurück.
Ich bin weder mit C# noch .NET vertraut, also wäre ich etwas in der Reihe von:
public List<string> Foo(List<string> list)
{
List<string> newList = (List<string>)list.Clone();
newList.Add("Bar");
return newList;
}
Auf diese Weise die Methode, die das aufruft Foo
Methode wird das neu erstellt List
kehrte zurück und das Original List
Das wurde nicht berührt.
Dies liegt wirklich dem "Vertrag" Ihrer Spezifikationen oder API List
S kann einfach geändert werden, ich sehe kein Problem mit dem ersten Ansatz.
Sie tun genau das Gleiche in beiden Methoden, nur einer von ihnen gibt dieselbe Liste zurück.
Es hängt meiner Meinung nach wirklich davon ab, was Sie tun. Stellen Sie einfach sicher, dass Ihre Dokumentation klar ist, was los ist. Schreiben Sie Vorkonditionen und Post-Konditionen, wenn Sie sich mit so etwas befassen.
Es ist eigentlich nicht so unerwartet, dass eine Methode, die eine Liste als Parameter einnimmt, die Liste ändert. Wenn Sie eine Methode wünschen, die nur aus der Liste liest, verwenden Sie eine Schnittstelle, die nur das Lesen ermöglicht:
public int GetLongest(IEnumerable<string> list) {
int len = 0;
foreach (string s in list) {
len = Math.Max(len, s.Length);
}
return len;
}
Durch die Verwendung einer Schnittstelle wie diese verbieten Sie die Methode nicht nur, die Liste zu ändern, sondern auch flexibler, da sie beispielsweise jede Sammlung verwenden kann, die die Schnittstelle implementiert, wie beispielsweise ein String -Array.
Einige andere Sprachen haben eine const
Schlüsselwort, das auf Parameter angewendet werden kann, um eine Methode zu verbieten, sie zu ändern. Da .NET Schnittstellen hat, die Sie dafür verwenden können, und Strings, die unveränderlich sind, gibt es nicht wirklich einen Bedarf an const
Parameter.
Das Aufkommen von Erweiterungsmethoden hat es etwas einfacher gemacht, mit Methoden umzugehen, die Nebenwirkungen einführen. Zum Beispiel wird es in Ihrem Beispiel viel intuitiver zu sagen
public static class Extensions
{
public static void AddBar(this List<string> list)
{
list.Add("Bar");
}
}
und ruf es mit
mylist.AddBar();
Das macht es klarer, dass mit der Liste etwas passiert.
Wie in den Kommentaren erwähnt, ist dies in Listen am nützlichsten, da Änderungen an einer Liste tendenziell verwirrender sein können. Bei einem einfachen Objekt würde ich dazu neigen, das an Ort und Stelle zu änderne Objekt nur zu ändern.