2009-06-26 4 views
5

J'écris un itérateur qui doit faire passer un entier mutable. Ceci me met "Erreur 476 Iterators ne peut pas avoir de paramètres ref ou out".Enveloppe Mutable des types de valeur à passer dans les itérateurs

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, quels que soient les appels Foo() ci-dessus veut savoir la valeur de fin de valueThatMeansSomething et Foo() peut l'utiliser lui-même. Vraiment, je veux un entier qui soit un type de référence et non un type de valeur. Tout ce que 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; } 
} 

Alors:

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

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

Y at-il une classe ou d'un mécanisme pour gérer ce déjà dans le BCL? Des défauts avec ValueWrapper<T> proposé ci-dessus?

(Mon utilisation réelle est plus complexe que l'exemple ci-dessus la manipulation si la variable dans ma boucle foreach qui appelle Foo() est pas une option. Période.)

Répondre

4

Non, je suis assez confiant il n'y a rien existant dans la BCL qui peut le faire. Votre meilleure option est précisément ce que vous avez proposé je pense. La mise en œuvre de ValueWrapper n'a vraiment pas besoin d'être plus compliquée que ce que vous avez proposé.

Bien sûr, ce n'est pas garanti pour les threads, mais si vous en avez besoin, vous pouvez simplement convertir la propriété automatique en propriété standard avec une variable backing et marquer le champ comme volatile (pour vous assurer que la valeur est à jour en tout temps).

+2

La création d'un champ volatile n'est pas suffisante pour assurer la sécurité des threads car les écritures sur des types de valeur arbitraires ne sont pas garanties par la spécification C#. Volatile ne garantit pas l'atomicité, il élimine simplement certains problèmes d'ordre induits par l'optimisation du compilateur. –

+0

Si vous vous souciez de la sécurité des filetages, utilisez des verrous. –

+0

@Eric: Oui, bon point. J'ai écrit à l'origine qu'il garantit l'atomicité, mais je l'ai rapidement supprimé car j'ai réalisé que ce n'était pas nécessairement le cas. – Noldorin

5

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

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

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

Coïncidence, je vais faire une série sur les raisons pour lesquelles il y a tant de restrictions loufoques sur des blocs de iterator dans mon blog en juillet. "Pourquoi pas de paramètres ref?" sera au début de la série.

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

0

J'ai longtemps pensé que la BCL devrait vraiment avoir une chose de classe et d'interface comme les suivantes:

 
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 beaucoup de gens enveloppent instinctivement les champs dans les propriétés d'auto, les champs permettent souvent plus propre et un code 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 termes efficaces et sémantiques, mais lorsque le but d'un type est d'être un objet de classe dont l'état est complètement exposé et mutable, une telle encapsulation est contre-productive.

L'interface est incluse non parce que plusieurs utilisateurs d'un MutableWrapper<T> voudraient utiliser l'interface à la place, mais plutôt parce qu'un IReadWriteActUpon<T> pourrait être utile dans une variété de situations, dont certaines impliqueraient l'encapsulation, et quelqu'un qui a une instance de MutableWrapper<T> pourrait vouloir le passer au code qui est conçu pour fonctionner avec des données encapsulées dans une interface IReadWriteActUpon<T>.

Questions connexes