Wenn ich die Diskussionen hier auf SO verfolge, habe ich schon mehrmals die Bemerkung gelesen, dass mutable structs "böse" sind (wie in der Antwort auf diese Frage ).
Was ist das eigentliche Problem mit Mutability und Structs in C#?
Wenn ich die Diskussionen hier auf SO verfolge, habe ich schon mehrmals die Bemerkung gelesen, dass mutable structs "böse" sind (wie in der Antwort auf diese Frage ).
Was ist das eigentliche Problem mit Mutability und Structs in C#?
Wenn etwas mutiert werden kann, erhält es eine Identität.
struct Person {
public string name; // mutable
public Point position = new Point(0, 0); // mutable
public Person(string name, Point position) { ... }
}
Person eric = new Person("Eric Lippert", new Point(4, 2));
Denn Person
veränderlich ist, ist es natürlicher, über Erics Position ändern als Eric klonen, den Klon verschieben und das Original zerstören . Beide Vorgänge würden den Inhalt von eric.position
aber das eine ist intuitiver als das andere. Ebenso ist es intuitiver, Eric weiterzugeben (als Referenz), damit die Methoden ihn ändern können. Einer Methode einen Klon von Eric zu geben, wird fast immer überraschend sein. Jeder, der mutieren möchte Person
muss daran denken, um eine Referenz zu bitten Person
oder sie tun das Falsche.
Wenn Sie den Typ unveränderlich machen, verschwindet das Problem; wenn ich nicht ändern kann eric
macht es für mich keinen Unterschied, ob ich eric
oder ein Klon von eric
. Allgemeiner ausgedrückt: Ein Typ kann sicher als Wert übergeben werden, wenn sein gesamter beobachtbarer Zustand in Elementen enthalten ist, die entweder:
Wenn diese Bedingungen erfüllt sind, verhält sich ein veränderlicher Werttyp wie ein Referenztyp, da eine oberflächliche Kopie dem Empfänger immer noch erlaubt, die ursprünglichen Daten zu ändern.
Die Intuitivität einer unveränderlichen Person
Das hängt allerdings davon ab, was Sie erreichen wollen. Wenn Person
stellt lediglich eine Datensatz über eine Person, so ist das nicht unintuitiv; Person
Variablen wirklich abstrakt darstellen Werte und nicht Objekte. (In diesem Fall wäre es wahrscheinlich angemessener, ihn umzubenennen in PersonData
.) Wenn Person
tatsächlich eine Person selbst modelliert, ist die Idee, ständig Klone zu erstellen und zu verschieben, albern, selbst wenn man den Fehler vermieden hat, zu denken, man würde das Original verändern. In diesem Fall wäre es wahrscheinlich sinnvoller, einfach Person
einen Referenztyp (d.h. eine Klasse).
Zugegeben, wie uns die funktionale Programmierung gelehrt hat, gibt es Vorteile, wenn man alles unveränderlich (niemand kann heimlich einen Verweis auf eric
und ihn mutieren), aber da das in der OOP nicht idiomatisch ist, wird es für alle anderen, die mit Ihrem Code arbeiten, trotzdem unintuitiv sein.
Es hat zwar nichts mit Structs zu tun (und auch nicht mit C#), aber in Java kann es zu Problemen mit veränderlichen Objekten kommen, wenn sie z.B. Schlüssel in einer Hash-Map sind. Wenn Sie sie ändern, nachdem Sie sie zu einer Map hinzugefügt haben, und die Map ändert ihre [Hash-Code](http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#hashCode()) könnten böse Dinge passieren.
Es gibt viele Vor- und Nachteile von veränderlichen Daten. Der millionenschwere Nachteil ist die Verfremdung. Wenn derselbe Wert an mehreren Stellen verwendet wird und an einer davon geändert wird, scheint er sich an den anderen Stellen, die ihn verwenden, auf magische Weise geändert zu haben. Dies ist mit Race Conditions verwandt, aber nicht identisch mit ihnen.
Der Millionen-Dollar-Vorteil ist die Modularität, manchmal. Ein veränderlicher Zustand kann es Ihnen ermöglichen, sich ändernde Informationen vor Code zu verbergen, der sie nicht kennen muss.
Die Kunst des Dolmetschers geht ausführlich auf diese Kompromisse ein und gibt einige Beispiele.
Wenn ich mir den Code ansehe, sieht das Folgende für mich persönlich ziemlich klobig aus:
data.value.set ( data.value.get () + 1 ) ;
und nicht einfach
data.value++ ; oder data.value = data.value + 1 ;
Die Datenkapselung ist nützlich, wenn Sie eine Klasse weitergeben und sicherstellen wollen, dass der Wert auf kontrollierte Weise geändert wird. Wenn Sie jedoch öffentliche "set"- und "get"-Funktionen haben, die kaum mehr tun, als den Wert auf das zu setzen, was auch immer übergeben wird, wie kann dies eine Verbesserung gegenüber der einfachen Weitergabe einer öffentlichen Datenstruktur sein?
Wenn ich eine private Struktur innerhalb einer Klasse erstelle, habe ich diese Struktur erstellt, um eine Reihe von Variablen in einer Gruppe zu organisieren. Ich möchte diese Struktur innerhalb des Klassenbereichs ändern können und nicht Kopien dieser Struktur erhalten und neue Instanzen erstellen.
Für mich verhindert dies eine gültige Verwendung von Strukturen, die zur Organisation öffentlicher Variablen verwendet werden; wenn ich Zugriffskontrolle wollte, würde ich eine Klasse verwenden.
Es gibt mehrere Probleme mit dem Beispiel von Herrn Eric Lippert. Es ist konstruiert, um zu verdeutlichen, dass Strukturen kopiert werden und dass das ein Problem sein kann, wenn man nicht vorsichtig ist. Wenn ich mir das Beispiel ansehe, sehe ich es als Ergebnis einer schlechten Programmiergewohnheit und nicht wirklich als ein Problem mit der Struktur oder der Klasse.
Eine Struktur soll nur öffentliche Mitglieder haben und sollte keine Kapselung erfordern. Wenn doch, dann sollte es wirklich ein Typ/Klasse sein. Man braucht wirklich nicht zwei Konstrukte, um das Gleiche zu sagen.
Wenn Sie eine Klasse haben, die eine Struktur umschließt, rufen Sie eine Methode in der Klasse auf, um das Mitglied Struktur zu ändern. Das würde ich als gute Programmiergewohnheit tun.
Eine ordnungsgemäße Umsetzung würde folgendermaßen aussehen.
struct Mutable {
public int x;
}
class Test {
private Mutable m = new Mutable();
public int mutate()
{
m.x = m.x + 1;
return m.x;
}
}
static void Main(string[] args) {
Test t = new Test();
System.Console.WriteLine(t.mutate());
System.Console.WriteLine(t.mutate());
System.Console.WriteLine(t.mutate());
}
Es sieht so aus, als ob es ein Problem mit der Programmiergewohnheit und nicht mit struct selbst ist. Strukturen sollen veränderbar sein, das ist die Idee und Absicht.
Das Ergebnis der Änderungen voila verhält sich wie erwartet:
1 2 3 Drücken Sie eine beliebige Taste, um fortzufahren . . .
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
25 Stimmen
Die Behauptung, veränderbare Strukturen seien böse, ist wie die Behauptung, veränderbare
int
s,bool
s, und alle anderen Werttypen sind böse. Es gibt Fälle für Veränderbarkeit und für Unveränderbarkeit. Diese Fälle hängen von der Rolle ab, die die Daten spielen, nicht von der Art der Speicherzuweisung/-freigabe.63 Stimmen
@slipp
int
ybool
sind pas wandelbar..2 Stimmen
.
-Syntax, wodurch Operationen mit ref-typed data und value-typed data gleich aussehen, obwohl sie sich deutlich unterscheiden. Dies ist ein Fehler von C#s Eigenschaften, nicht von Structs - einige Sprachen bieten eine alternativea[V][X] = 3.14
Syntax für In-Place-Mutationen. In C# ist es besser, Struktur-Member-Mutator-Methoden wie 'MutateV(Action<ref Vector2> mutator)` anzubieten und sie wie folgt zu verwendena.MutateV((v) => { v.X = 3; })
(Das Beispiel ist aufgrund der Einschränkungen, die C# hinsichtlich derref
Schlüsselwort, sollte aber mit einigen Umgehungen möglich sein) .0 Stimmen
@Sushi271 Umgehung (n): Eine Methode zur Überwindung eines Problems oder einer Einschränkung in einem Programm oder System. Dies ist eine Einschränkung in der Syntax von C#. Die ideale Verwendung der
ref
Schlüsselwort in einem generischen Typ ist nicht zweideutig; es wird nur nicht unterstützt. Das ist eine Einschränkung. Und ja, C# erzwingt in vielerlei Hinsicht eine unbequeme Syntax, um die Arbeit zu erledigen. Das klassischste Beispiel für die Unbeholfenheit von C# istpublic
immer und immer wieder für jedes Mitglied zu verwenden, anstatt eine C++-ähnlichepublic:
Präfix oder ein separates Ruby-artigespublic FieldName1, FieldName2, MethodName1, MethodName2;
0 Stimmen
@Sushi271 Strukturen mit vielen Mitgliedern sind wegen der Ineffizienz des Kopierens vieler Datenelemente nicht üblich. Die Anwendungsfälle sind die gleichen, aber gute Programmierer werden (und haben, historisch gesehen) einen anderen Ansatz wählen, um die Effizienz zu steigern. Also, ja, leichtgewichtig ist in der Regel besser für jeden Wert-typed Daten.
3 Stimmen
@Slipp Nun, ich denke genau das Gegenteil über diese Art von Strukturen. Warum denkst du, dass Strukturen, die bereits in der .NET Bibliothek implementiert sind, wie DateTime oder TimeSpan (also ähnliche) unveränderlich sind? Vielleicht könnte es nützlich sein, nur ein Mitglied solcher struct's var zu ändern, aber es ist einfach zu unbequem, führt zu zu vielen Problemen. Eigentlich sind Sie falsch über das, was tut der Prozessor berechnen, da C# nicht zu Assembler kompilieren, es kompiliert zu IL. In IL (vorausgesetzt, wir haben bereits die Variable namens
x
) umfasst dieser einzelne Vorgang 4 Anweisungen:ldloc.0
(lädt die Variable 0-Index in...1 Stimmen
... Typ.
T
Typ ist. Ref ist nur ein Schlüsselwort, das die Variable, die an eine Methode übergeben wird, zu einer Methode selbst macht, nicht zu einer Kopie davon. Es ist auch sinnvoll für die Referenztypen, da wir die die Variable d.h. der Verweis außerhalb der Methode wird auf ein anderes Objekt zeigen, nachdem er innerhalb der Methode geändert wurde. Daref T
ist kein Typ, aber die Art und Weise der Übergabe eines Methodenparameters kann man nicht in<>
denn dort können nur Typen abgelegt werden. Also ist es einfach falsch. Vielleicht wäre es praktisch, dies zu tun, vielleicht das C#-Team könnte dies für einige neue Version zu machen, aber im Moment sind sie auf einige arbeiten...1 Stimmen
... viel wichtigere Dinge wie Null-Bedingungsoperatoren oder Auto-Eigenschaftsinitialisierungen. Was die
public
... Ich mag eigentlich die C# Art, dies zu tun. Es ist tatsächlich einfacher zu refaktorisieren, weil das Löschen/Hinzufügen einer Public an einer Stelle für ein Mitglied sich nicht auf alle anderen Mitglieder auswirkt. In C++ muss ich oft zwei Labels hinzufügen (z.B. public: und private:), oder die Methode in einen anderen Teil der Klasse verschieben. Das ist lästig. Jedenfalls wurde mir durch die Erwähnung dieses Themas klar, dass wir diese Diskussion beenden sollten. Wir fangen an, uns über unsere eigenen Meinungen zu streiten, und wir könnten uns jahrelang streiten, ohne eine gemeinsame Meinung zu finden.1 Stimmen
Boden. Jeder hat das Recht auf seine eigene Meinung. Sie, Sir, scheinen mehr an C++ gebunden zu sein, während meine Hauptsprache in den letzten 8 Jahren C# war. Es ist sicherlich lehrreich, den Standpunkt eines anderen zu bestimmten Themen zu erfahren, aber im Moment sind wir weit vom Hauptthema abgewichen. Vielen Dank also für Ihren Beitrag.
1 Stimmen
Wir schreiben das Jahr 2017 und die veränderbaren Strukturen sind zurück! :D Wie immer geht es um das Wissen, wie und wann man sie verwendet. Das heißt, ich würde wahrscheinlich jedem Anfänger sagen "Halten Sie sich von structs fern oder machen Sie sie zumindest veränderbar - kommen Sie später darauf zurück, wenn Sie mehr Erfahrung haben, sie bieten besondere Vorteile" . (Zu verstehen, dass eine Referenz ein spezieller Adresswerttyp mit speziellen Operatorüberladungen ist, löst alles, aber das ist für Anfänger einfach zu viel zu kauen).
1 Stimmen
Ich stimme @AnorZaken zu, dass veränderbare Strukturen nur dann "böse" sind, wenn man den Unterschied zwischen Wert- und Referenztypen nicht kennt, ansonsten kann man Strukturen verwenden, auch wenn sie veränderbar sind, solange man weiß, was man tut. (btw .NET selbst hat viele veränderbare Strukturen)
0 Stimmen
Diese Frage hätte schon vor Jahren gesperrt werden sollen, da sie hauptsächlich auf Meinungen beruht. Ob Sie gültige Anwendungsfälle von veränderbaren Strukturen ignorieren wollen oder nicht, ist allein Ihre Entscheidung als Programmierer. Aber darüber zu debattieren, ob etwas, das es gibt, existieren sollte oder nicht, hat hier auf SO keinen Sinn, insbesondere wenn man bedenkt, dass es eine Fülle von Ressourcen gibt, die es jedem ermöglichen, diese Unterscheidung selbst zu treffen.