2009-06-26 8 views
5

Ich schreibe einen Iterator, der eine veränderliche Ganzzahl übergeben muss.Veränderlicher Wrapper von Werttypen, die an Iteratoren übergeben werden können

public IEnumerable<T> Foo(ref int valueThatMeansSomething) 
{ 
    // Stuff 

    yield return ...; 
} 

Dies ordnet mich "Error 476 Iteratoren können ref oder out Parameter nicht haben".

Was ich brauche, ist dieser Integerwert, der im Iterator geändert und vom Aufrufer des Iterators verwendet werden kann. Mit anderen Worten, was immer Foo() anruft, möchte den Endwert von valueThatMeansSomething und Foo() auch selbst verwenden. Wirklich, ich möchte eine Ganzzahl, die ein Referenztyp ist, kein Werttyp.

Ich kann nur an eine Klasse denken, die meine Ganzzahl kapselt und mir erlaubt, sie zu ändern.

public class ValueWrapper<T> 
    where T : struct 
{ 
    public ValueWrapper(T item) 
    { 
     this.Item = item; 
    } 

    public T Item { get; set; } 
} 

So:

ValueWrapper<int> w = new ValueWrapper<int>(0); 
foreach(T item in Foo(w)) 
{ 
    // Do stuff 
} 

if (w.Item < 0) { /* Do stuff */ } 

Gibt es eine Klasse oder einen Mechanismus dies bereits in der BCL zu behandeln? Irgendwelche Fehler mit ValueWrapper<T> oben vorgeschlagen?

(Meine tatsächliche Nutzung ist komplizierter als das obige Beispiel so die Variable in meiner foreach Schleife der Handhabung Foo() Anrufe ist keine Option. Period.)

Antwort

4

Nö, ich bin ziemlich zuversichtlich, nichts in der bestehenden es BCL, die dies tun können. Ihre beste Option ist genau das, was Sie vorgeschlagen haben, denke ich. Die Implementierung von ValueWrapper muss wirklich nicht komplizierter sein als das, was Sie vorgeschlagen haben.

Natürlich ist es nicht garantiert Threadsicher zu sein, aber wenn Sie das brauchen, können Sie einfach die automatische Eigenschaft in eine Standardvariable umwandeln und das Feld als volatile markieren (um sicherzustellen, dass der Wert hoch ist). immer aktuell).

+2

Wenn Sie ein flüchtiges Feld erstellen, ist dies nicht ausreichend, um die Threadsicherheit zu gewährleisten, da die C# -Spezifikation nicht garantiert, dass Schreibvorgänge auf beliebige Werttypen atomar sind. Volatile garantiert keine Atomizität, es eliminiert nur einige Probleme, die durch die Optimierung der Compiler verursacht werden. –

+0

Wenn Sie sich für die Sicherheit von Gewinden interessieren, verwenden Sie Schlösser. –

+0

@Eric: Ja, guter Punkt. Ich schrieb ursprünglich, dass es Atomarität garantiert, aber dann schnell entfernte es, als ich erkannte, dass dies nicht unbedingt die Fälle war. – Noldorin

5

Wenn Sie nur den Wert schreiben müssen, dann wäre eine andere Technik sein:

public IEnumerable<whatever> Foo(Action<int> setter) { ... } 

int value = 0; 
foreach(var x in Foo(x => {value=x;}) { ... } 

Zufälligerweise werde ich auf die Gründe, die eine Reihe tun, warum es auf Iterator Blöcke in meinem Blog so viele doof Einschränkungen im Juli. "Warum keine Ref-Parameter?" wird früh in der Serie sein.

http://blogs.msdn.com/ericlippert/archive/tags/Iterators/default.aspx

0

ich lange gedacht, dass der BCL wirklich eine Klasse und Schnittstelle so etwas wie die folgenden haben sollte:

 
public delegate void ActByRef<T1,T2>(ref T1 p1); 
public delegate void ActByRefRef<T1,T2>(ref T1 p1, ref T2 p2); 
public interface IReadWriteActUpon<T> 
{ 
    T Value {get; set;} 
    void ActUpon(ActByRef<T> proc); 
    void ActUpon<TExtraParam>(ActByRefRef<T, TExtraParam> proc, 
          ref TExtraparam ExtraParam); 
} 

public sealed class MutableWrapper<T> : IReadWrite<T> 
{ 
    public T Value; 
    public MutableWrapper(T value) { this.Value = value; } 
    T IReadWriteActUpon<T>.Value {get {return this.Value;} set {this.Value = value;} } 
    public void ActUpon(ActByRef<T> proc) 
    { 
    proc(ref Value); 
    } 
    public void ActUpon<TExtraParam>(ActByRefRef<T, TExtraParam> proc, 
          ref TExtraparam ExtraParam) 
    { 
    proc(ref Value, ref ExtraParam); 
    } 
} 

Obwohl viele Menschen instinktiv wickeln Felder in Auto-Eigenschaften, Felder erlauben oft sauberer und effizienterer Code, insbesondere bei Verwendung von Werttypen. In vielen Situationen kann die erhöhte Einkapselung, die man durch Verwendung von Eigenschaften gewinnen kann, die Kosten in Effizienz und Semantik wert sein, aber wenn der ganze Zweck eines Typs ein Klassenobjekt sein soll, dessen Zustand vollständig offen und veränderlich ist, ist eine solche Einkapselung kontraproduktiv.

Die Schnittstelle ist nicht enthalten, da viele Benutzer eines MutableWrapper<T> möchte stattdessen die Schnittstelle verwenden, sondern weil ein IReadWriteActUpon<T> in einer Vielzahl von Situationen nützlich sein könnte, der eine Instanz hat, von denen einige mit sich bringen Verkapselung würde, und jemand von MutableWrapper<T> könnte es an Code übergeben, der mit Daten arbeiten soll, die in einer IReadWriteActUpon<T>-Schnittstelle gekapselt sind.

Verwandte Themen