2012-08-23 2 views
18

Ist so etwas möglich? Ich gehe davon aus nicht, aber es sieht gut aus mir:Eigenschaft Hintergrund Wertbereich

class MyClass { 
    public int Foo { 
     get { return m_foo; } 
     set { 
      // Bounds checking, or other things that prevent the use 
      // of an auto-implemented property 
      m_foo = value; 
     } 

     // Put the backing field actually *in* the scope of the property 
     // so that the rest of the class cannot access it. 
     private int m_foo; 
    } 

    void Method() { 
     m_foo = 42; // Can't touch this! 
    } 
} 

Natürlich weiß ich diese Syntax falsch ist, und dies wird nicht kompiliert. Es war hypothetisch-Zukunft-C#, um meine Idee klar darzustellen. Ich entschuldige mich für die etwas hypothetische Frage, aber es ist zu spezifisch für Programmers.SE.

So etwas wie dies im Compiler implementiert werden könnte, die einen Zweck dienen würden: Nur das get Eigentum ermöglichen und set Accessoren das Feld zu sehen, die im Wesentlichen die Eigenschaft ermöglicht umluftunabhängigem zu sein (wie automatisch implementierte Eigenschaften sind), während Erlaube zusätzliche Get/Set-Logik.

+2

Ist dies eine einfache Ja/Nein-Frage (denn dann ist es ** nein **), oder wollen Sie alternative Lösungen für dieses "Problem"? –

+0

Ich bin nur neugierig, was Sie damit erreichen wollen? –

+0

Es wäre sehr interessant mit alternativen Lösungen. Es könnte ein nettes Feature sein, um die Verwendung der Eigenschaft und nicht der Variablen in der Klasse selbst zu erzwingen. Denn das muss das Ziel sein, oder? –

Antwort

31

Die kurze Antwort ist nein, das ist heute in C# nicht möglich.

Wir bekommen eine Feature-Anfrage wie diese ziemlich oft; es ist ein nettes Feature in seiner allgemeineren Form. Die allgemeinere Form besteht darin, die Lebenszeit einer lokalen Variablen orthogonal zu ihrem Scope deutlicher zu machen.

Nur um sicherzustellen, dass diese Begriffe klar sind: Eine Variable ist ein Speicherort, möglicherweise benannt.Jede Variable hat eine Lebenszeit: die Menge an Zeit zur Laufzeit, in der sich die Variable garantiert auf gültigen Speicher bezieht. Der Bereich eines Namens ist der Bereich des Texts, in dem dieser Name verwendet werden darf; Es ist ein Kompilierzeitkonzept, kein Laufzeitkonzept. Eine lokale Variable ist eine Variable, deren Bereich ein Anweisungsblock ist. In vielen Sprachen ist die Lebensdauer einer lokalen Variablen eng mit ihrem Gültigkeitsbereich verknüpft: Wenn die Steuerung den Bereich zur Laufzeit logisch erreicht, beginnt die Lebensdauer und wenn sie den Bereich verlässt, endet die Lebensdauer. Dies gilt in C# mit einigen bemerkenswerten Einschränkungen:

  • Die Lebensdauer eines lokalen verlängert werden kann oder abgeschnitten, wenn die Laufzeit bestimmen kann, dass dies hat keine Auswirkung auf die Aktion von verwalteten Code auf dem aktuellen Thread . Die Aktionen anderer Threads (wie der Finalizer-Thread) und nicht verwalteten Codes im aktuellen Thread sind implementierungsdefiniert.

  • Die Lebensdauer eines lokalen Objekts in einem Iteratorblock, einer asynchronen Methode oder einer geschlossenen äußeren Variablen einer anonymen Funktion kann erweitert werden, um die Lebensdauer des Iterators, Tasks, Stellvertreters oder Ausdrucksbaum, der es verwendet.

Natürlich ist es keine Anforderung, dass die Lebensdauer und den Umfang eines lokalen zusammen in irgendeiner Weise gebunden werden. Es wäre schön, wenn Locals explizit die Lebensdauer einer Instanz oder eines statischen Felds hätten, aber der Geltungsbereich eines Locals. C hat diese Eigenschaft; Sie können eine "statische" lokale Variable erstellen. C# nicht. Ihr Vorschlag besteht im Wesentlichen darin, eine lokale Variable innerhalb des Blocks der Eigenschaft zuzulassen, die die Lebensdauer der Instanz hat, deren Geltungsbereich jedoch auf den Block beschränkt ist.

Ich würde diese Funktion als "nett" klassifizieren. Wir haben eine Liste potentieller "netter" Features, buchstäblich so lange wie Ihr Arm, für die wir keine Zeit haben, zu implementieren, also würde ich nicht erwarten, dass dieser in nächster Zeit an die Spitze der Liste kommt. Danke für das Feedback. Es hilft uns, diese Liste etwas zu priorisieren.

+1

Frage: Gibt es eine einfache allgemeine Änderung am Design der C# -Sprache, die Variable entkoppeln würde? Lebensdauer und Umfang? Sie weisen darauf hin, dass Variablen im Eigenschaftsbereich (OP-Frage) und statische Locals in C (Ihr Beispiel) spezielle Fälle der Entkopplung der Lebensdauer vom Geltungsbereich sind, aber ich kann mir nicht vorstellen, was der allgemeine Fall wäre. – phoog

1

Nein, das einzige, was innerhalb des Körpers der Eigenschaft sein kann, ist die get und set.

2

Gemäß den C# 4.0-Sprachspezifikationen.

Im Gegensatz zu Feldern bezeichnen die Eigenschaften keine Speicherorte. Stattdessen haben Eigenschaften Zugriffsmethoden, die angeben, dass die Anweisungen ausgeführt werden, wenn ihre Werte gelesen oder geschrieben werden.

Das Hinzufügen eines Feldes würde einen Speicherort erfordern. Also nein, das ist nicht möglich.

+1

Ich verstehe, was Sie sagen, aber das trifft hier nicht wirklich zu.Es geht mir nur um den lexikalischen Umfang des Feldes, nicht darum, wo es gespeichert ist. Natürlich gehen Eigenschaften nur auf die Methoden '_get()' und '_set()' über, so dass das Feld immer noch im Kontext der Klasse enthalten wäre. –

+0

@ JonathonReinhart - Wenn der enthaltene Typ nicht die Eigenschaft ist, welches Dienstprogramm bietet das Konzept dann? Der enthaltende Typ, z.B. Die Klasse kann weiterhin auf das private Feld zugreifen. Dies wirft auch die Frage auf, ob eine Eigenschaft überhaupt als ein containing type angesehen werden kann. –

+2

Ich sage nur, dass so etwas in dem Compiler implementiert werden könnte, der einem Zweck dienen würde: Erlaube den 'get'- und 'set'-Zugriffsmethoden der Eigenschaft nur, das Feld zu sehen, was im Wesentlichen die Eigenschaft erlaubt, in sich abgeschlossen zu sein Auto-implementierten Eigenschaften sind), während zusätzliche Logik erlaubt. –

1

Nun, es ist ziemlich schwierig zu handhaben, wahrscheinlich nicht sehr performant, und nicht etwas, was ich wirklich verwenden würde, aber technisch ist es eine Möglichkeit, das Hintergrundfeld vom Rest der Klasse zu verdecken.

public class MySuperAwesomeProperty<T> 
{ 
    private T backingField; 
    private Func<T, T> getter; 
    private Func<T, T> setter; 
    public MySuperAwesomeProperty(Func<T, T> getter, Func<T, T> setter) 
    { 
     this.getter = getter; 
     this.setter = setter; 
    } 

    public T Value 
    { 
     get 
     { 
      return getter(backingField); 
     } 
     set 
     { 
      backingField = setter(value); 
     } 
    } 
} 

public class Foo 
{ 
    public MySuperAwesomeProperty<int> Bar { get; private set; } 


    public Foo() 
    { 
     Bar = new MySuperAwesomeProperty<int>(
      value => value, value => { doStuff(); return value; }); 

     Bar.Value = 5; 

     Console.WriteLine(Bar.Value); 
    } 

    private void doStuff() 
    { 
     throw new NotImplementedException(); 
    } 
} 
+0

Es gibt nichts, was Sie davon abhält, das Hintergrundfeld innerhalb der MySuperAwesomeProperty-Klasse zu setzen. Dies vermisst auch den Punkt der ursprünglichen Frage IMO. – user981225

+0

@ user981225 Der ganze Punkt der Eigenschaft Klasse ist, dass es versiegelt und irgendwo enthalten ist, nicht geändert werden. Andere Klassen verwenden sie einfach anstelle einer Eigenschaft des tatsächlichen Werts, um benutzerdefinierte Getter/Setter ohne Zugriff auf das Hintergrundfeld zuzulassen. Es verhindert nicht die Modifikation aufgrund von Reflektion, aber auf dieser Ebene kann sogar auf das Hintergrundfeld einer regulären Eigenschaft zugegriffen werden. Wie ist das nicht der Punkt der ursprünglichen Frage? – Servy

+0

Entschuldigung, Sie haben Recht. Wenn du den Post redigierst, kann ich upvoten. – user981225

6

Hier ist mein nehmen auf, dass:

public class WrappedField<T> 
{ 
    public class Internals 
    { 
     public T Value; 
    } 

    private readonly Internals _internals = new Internals(); 
    private readonly Func<Internals, T> _get; 
    private readonly Action<Internals, T> _set; 

    public T Value 
    { 
     get { return _get(_internals); } 
     set { _set(_internals, value); } 
    } 

    public WrappedField(Func<Internals, T> get, Action<Internals, T> set) 
    { 
     _get = get; 
     _set = set;    
    } 

    public WrappedField(Func<Internals, T> get, Action<Internals, T> set, T initialValue) 
     : this(get, set) 
    { 
     _set(_internals, initialValue); 
    } 
} 

Verbrauch:

class Program 
{ 
    readonly WrappedField<int> _weight = new WrappedField<int>(
     i => i.Value,   // get 
     (i, v) => i.Value = v, // set 
     11);     // initialValue 

    static void Main(string[] args) 
    { 
     Program p = new Program(); 
     p._weight.Value = 10; 

     Console.WriteLine(p._weight.Value); 
    } 
} 
+0

Sie können auch benutzerdefinierte implizite Konvertierung implementieren, so dass der Benutzercode nicht betroffen ist und Sie nicht varname.Value verwenden müssen, um den Wert der Eigenschaft festzulegen und abzurufen – Sonia

2

Wenn Sie Generika vermeiden möchten, können Sie immer die _backingField verstecken und die Kontrolle Grenzen in einem privaten innere Klasse. Sie könnten es sogar noch weiter verstecken, indem Sie die äußere Klasse partiell machen. Natürlich müsste zwischen der äußeren und der inneren Klasse etwas Delegieren stattfinden, was ein Mist ist. Code meine Gedanken zu erklären:

public partial class MyClass 
{ 
    public int Property 
    { 
     get { return _properties.Property; } 
     set { _properties.Property = value; } 
    } 

    public void Stuff() 
    { 
     // Can't get to _backingField... 
    } 
} 

public partial class MyClass 
{ 
    private readonly Properties _properties = new Properties(); 

    private class Properties 
    { 
     private int _backingField; 

     public int Property 
     { 
      get { return _backingField; } 
      set 
      { 
       // perform checks 
       _backingField = value; 
      } 
     } 
    } 
} 

Aber das ist eine Menge Code. Um all diese Kesselplatte zu rechtfertigen, muss das ursprüngliche Problem ziemlich streng sein ...

+0

Warum sollte jemand Generika vermeiden wollen? – Grozz

+0

@Grozz Ich weiß es nicht. Ich würde es nicht tun. Ich habe Servys Antwort sogar aufgestockt. Aber es gab bereits zwei Antworten, die auf Generika basierten, also dachte ich mir "Was ist das?" ... :-) –