2 Stimmen

Warum verhalten sich Eigenschaften auf diese Weise?

Von http://csharpindepth.com/Articles/Chapter8/PropertiesMatter.aspx

using System;

struct MutableStruct
{
    public int Value { get; set; }

    public void SetValue(int newValue)
    {
        Value = newValue;
    }
}

class MutableStructHolder
{
    public MutableStruct Field;
    public MutableStruct Property { get; set; }
}

class Test
{    
    static void Main(string[] args)
    {
        MutableStructHolder holder = new MutableStructHolder();
        // Beeinflusst den Wert von holder.Field
        holder.Field.SetValue(10);
        // Ruft holder.Property als Kopie ab und ändert die Kopie
        holder.Property.SetValue(10);

        Console.WriteLine(holder.Field.Value);
        Console.WriteLine(holder.Property.Value);
    }
} 

1) Warum wird eine Kopie (von Property?) gemacht?
2) Wenn ich den Code zu holder.Field.Value und holder.Property.Value = 10 ändere, erhalte ich den untenstehenden Fehler. Das hat mich wirklich umgehauen.

Der Rückgabewert von 'MutableStructHolder.Property' kann nicht verändert werden, da es keine Variable ist

Warum sollte es jemals verboten sein, einen Wert innerhalb einer Eigenschaft zuzuweisen?!? Beide Eigenschaften sind get/set!

Und schließlich WARUM würde man jemals das in 1 und 2 erwähnte Verhalten wollen? (Das ist mir nie passiert, ich habe immer nur Eigenschaften mit get verwendet).

Bitte erklären Sie es ausführlich, ich kann mir nicht vorstellen, jemals das 2. und noch weniger das erste zu wollen. Es ist einfach so merkwürdig für mich.

3voto

Zach Johnson Punkte 22572

1) Eine Kopie von Property wird erstellt, weil es sich um eine struct handelt, und eine struct ist ein Werttyp (im Gegensatz zu einer class), der nach Wert und nicht nach Referenz kopiert wird.

2) Der Fehler "Kann den Rückgabewert nicht ändern", den Sie sehen, stammt von der 'Kopie nach Wert'-Natur von structs. Wenn Sie die struct ändern würden (indem Sie Value auf der MutableStruct über Property auf MutableStructHolder setzen), würden Ihre Änderungen nicht reflektiert, da Sie eine Kopie der struct modifizieren würden, nicht die struct, die in der Eigenschaft gehalten wird. Da Sie dieses Verhalten nie möchten, verhindert der C# Compiler, dass Sie dies tun.

Wenn MutableStruct eine class wäre, gäbe es überhaupt kein Problem, sie durch eine Eigenschaft zu ändern (und der C# Compiler würde Ihnen das erlauben), da Sie mit der tatsächlichen Instanz arbeiten würden und nicht mit einer Kopie.

2voto

Kasper Holdum Punkte 12301

Frage 1: Sie sollten sich mit Strukturen und Klassen vertraut machen, da die Funktionsweise dieser alle von Ihnen beschriebenen "Symptome" erklärt. Strukturen werden nicht per Referenz weitergegeben, sondern tatsächlich durch das Erstellen von Kopien der weitergegebenen Objekte - Kopien ist das Stichwort. Wenn eine Eigenschaft eine Struktur zurückgibt, handelt es sich tatsächlich um eine Kopie und nicht um das "echte" Objekt, dessen Wert Sie ändern.

Was Frage 2 betrifft: Der C#-Compiler erkennt, dass das direkte Setzen einer Variable auf einem Struktur-Objekt, das über eine Eigenschaft abgerufen wird, sinnlos ist, und weist Sie daher mit einem Fehler darauf hin. Ob das über die Eigenschaft abgerufene Objekt einen Setter hat oder nicht, ändert dieses Verhalten nicht und Sie erhalten in beiden Fällen den Fehler.

Statt einer Erklärung zu Klassen vs Strukturen empfehle ich Ihnen, die Antwort von Lasse Karlsen auf die Frage zu lesen "Der Unterschied zwischen Strukturen und Klassen".

2voto

Ben Voigt Punkte 268424

Um auf Frage #1 zu antworten: Bedenken Sie, wie es ohne eine automatisch generierte Eigenschaft aussehen würde. Sie hätten Eigenschaft { get { return xxx; } } Das ist, wo die Kopie gemacht wird. Was wie das Verweisen auf eine Eigenschaft aussieht, ruft wirklich eine Getter-Methode auf. Würden Sie eine In-Place-Bearbeitung erwarten, wenn Sie Folgendes hätten:

private MutableStruct v;
public MutableStruct f()
{
  return v;
}

f() = new MutableStruct();

Natürlich nicht, wir alle verlassen uns darauf, dass das Return-Wort eine Kopie der Struktur macht, damit die private Variable gegen äußere Änderungen geschützt ist. Deshalb funktioniert es so.

Wenn Sie nun denken, dass der Eigenschaftssetter automatisch mit dem resultierenden Wert aufgerufen worden sein sollte, lassen Sie mich bitte darauf hinweisen, dass es extrem schwer unmöglich ist zu wissen, ob ein Aufruf einer der Methoden der Struktur eine Änderung bewirkt (denken Sie daran, wenn die Struktur in einer anderen Assembly definiert ist). Und das Zurückschreiben des Werts ist nicht nur ineffizient, sondern auch schädlich, es könnte die Korrektheit beeinträchtigen. Bedenken Sie ein Multithreading-Szenario, bei dem Threads, die nur lesend auf die geschützte Eigenschaft zugreifen, plötzlich darauf zurückgreifen. Allerlei Schmerzen und Leiden wären die Folge. Daher ist es keine Option, dass der C#-Compiler die Eigenschaft automatisch auf das Ergebnis Ihrer Berechnung setzt.

EDIT: Sie haben sich gefragt, warum das Setzen eines Feldes auf der Struktur eine Fehlermeldung generiert, das Aufrufen einer Methode, um dasselbe zu tun, jedoch nicht. Nun, die Methode könnte nützliche Seiteneffekte haben, also muss man sie immer noch auf einer temporären Kopie der Struktur aufrufen. Betrachten Sie die folgende Variation:

struct Mutable
{
    public static int Sum = 0;

    public int x;
    public Mutable(int x) { this.x = x; }
    public void Total() { Sum += x; }
}

class Container
{
    public Mutable Field;
    public Mutable Property { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Container c = new Container();
        c.Field = new Mutable(1);
        c.Property = new Mutable(2);
        c.Field.Total();
        c.Property.Total();
        Console.Out.WriteLine(Mutable.Sum);
    }
}

0voto

Zano Punkte 2555

Strukturen werden nach Wert übergeben, daher hast du immer nur Kopien. Außerdem:

public int Wert { get; set; }

ist eine Abkürzung für

private int _Wert;
public int Wert { 
   get { return _Wert; }  
   set { _Wert = value; }
}

Wie man sehen kann, gibt es viele Kopien, daher sind die Beschwerden zu erwarten. Wenn du stattdessen class verwendest, was ein Verweistyp ist, wirst du dieses Problem nicht haben.

CodeJaeger.com

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.

Powered by:

X