Frage

Ich schreibe eine Iterator, um eine veränderliche ganze Zahl passieren muss.

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

    yield return ...;
}

Diese Netze me "Fehler 476 Iteratoren nicht ref oder out-Parameter haben kann".

Was ich brauche, ist dieser Integer-Wert im Iterator und nutzbar durch den Anrufer des Iterators geändert werden. Mit anderen Worten, was Foo() oben ruft will den Endwert von valueThatMeansSomething kennen und Foo() kann es selbst verwenden. Wirklich, ich möchte eine ganze Zahl, die ein Referenztyp nicht ein Werttyp ist.

Das einzige, was ich denken kann, ist eine Klasse zu schreiben, die meine ganze Zahl kapselt und es mir erlaubt, ihn 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? Alle Fehler mit ValueWrapper<T> vorgeschlagen oben?

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

War es hilfreich?

Lösung

Nein, ich bin ziemlich sicher, es gibt nichts in den BCL bestehenden, kann dies tun. Ihre beste Möglichkeit ist genau das, was Sie vorgeschlagen haben, denke ich. Die Implementierung von ValueWrapper wirklich braucht, nicht komplizierter zu sein als das, was Sie vorgeschlagen haben.

Natürlich ist es nicht sein, Thread-sicher gewährleistet ist, aber wenn Sie benötigen, dass Sie einfach die automatische Eigenschaft in eine Standard eines mit einer Träger Variable umwandeln können und das Feld als volatile (den Wert, um sicherzustellen, ist up-to-date zu allen Zeiten).

Andere Tipps

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 im Juli so viele doof Einschränkungen. „Warum keine ref Parameter?“ wird in der Serie früh.

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

Ich habe 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 oft erlauben sauberer und effizienter Code vor allem, wenn Werttypen verwenden. In vielen Situationen kann die erhöhte Verkapselung eines gewinnen, indem sie Eigenschaften mit Hilfe einer effizienten und Semantik die Kosten wert sein kann, aber wenn der ganze Zweck eines Typs ist ein Klasse-Objekt, dessen Zustand zu sein, wird vollständig ausgesetzt und wandelbar, wie Verkapselung ist kontraproduktiv.

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top