Question

J'écris un itérateur qui doit faire passer un entier mutable.

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

    yield return ...;
}

Cela ne me rapporte pas & "; Erreur 476 Les itérateurs ne peuvent pas avoir de paramètres ref ou out &";

.

Ce dont j'ai besoin, c'est que cette valeur entière soit modifiée dans l'itérateur et utilisable par l'appelant de l'itérateur. En d'autres termes, tous les appels Foo() ci-dessus veulent connaître la valeur finale de valueThatMeansSomething et ValueWrapper<T> peuvent l'utiliser eux-mêmes. Vraiment, je veux un entier qui est un type de référence et non un type de valeur.

La seule chose à laquelle je peux penser est d'écrire une classe qui encapsule mon entier et me permet de le modifier.

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

    public T Item { get; set; }
}

Donc:

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

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

Existe-t-il déjà une classe ou un mécanisme pour gérer cela dans la BCL? Des failles avec foreach proposées ci-dessus?

(Mon utilisation réelle étant plus compliquée que l'exemple ci-dessus, la gestion de la variable dans ma <=> boucle qui appelle <=> n'est pas une option. Période.)

Était-ce utile?

La solution

Non, je suis convaincu que rien dans la BCL n’est capable de le faire. Votre meilleure option est précisément ce que vous avez proposé, je pense. La mise en œuvre de ValueWrapper ne doit en réalité pas être plus compliquée que ce que vous avez proposé.

Bien sûr, la sécurité du thread n'est pas garantie, mais si vous en avez besoin, vous pouvez simplement convertir la propriété automatique en propriété standard avec une variable de sauvegarde et marquer le champ comme volatile (pour garantir que la valeur est à jour à tout moment).

Autres conseils

Si vous avez seulement besoin d'écrire la valeur, une autre technique serait:

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

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

Par coïncidence, je vais faire une série sur les raisons pour lesquelles il y a tant de restrictions loufoques sur les blocs d'itérateurs dans mon blog en juillet. " Pourquoi pas de paramètres de référence? " sera au début de la série.

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

J'ai longtemps pensé que la BCL devrait avoir une classe et une interface similaires à celles-ci:

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);
  }
}

Bien que de nombreuses personnes insèrent instinctivement des champs dans des propriétés automatiques, ils permettent souvent un code plus propre et plus efficace, en particulier lors de l'utilisation de types de valeur. Dans de nombreuses situations, l’encapsulation accrue que l’on peut obtenir en utilisant des propriétés peut valoir le coût en efficacité et en sémantique, mais lorsque l’objectif d’un type est d’être un objet de classe dont l’état est complètement exposé et modifiable, une telle encapsulation est contre-productive.

L’interface est incluse, non pas parce que de nombreux utilisateurs de MutableWrapper<T> souhaitent plutôt l’utiliser, mais parce qu’un IReadWriteActUpon<T> pourrait être utile dans diverses situations, dont certaines nécessiteraient une encapsulation, et une instance de <=> peut souhaiter la transmettre à un code conçu pour fonctionner avec des données encapsulées dans une interface <=>.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top