2009-06-26 9 views
5

Escribo un iterador que debe pasar un entero mutable.Contenedor mutable de tipos de valores para pasar a los iteradores

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

    yield return ...; 
} 

Esto me dice "Error 476 Los iteradores no pueden tener parámetros de ref or out".

Lo que necesito es este valor entero para ser modificado en el iterador y utilizable por la persona que llama del iterador. En otras palabras, cualquier llamada Foo() anterior quiere saber el valor final de valueThatMeansSomething y Foo() puede usarlo. Realmente, quiero un entero que sea un tipo de referencia, no un tipo de valor.

Lo único que se me ocurre es escribir una clase que encapsule mi número entero y me permita modificarlo.

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

    public T Item { get; set; } 
} 

Así:

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

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

¿Hay alguna clase o mecanismo para manejar esto ya en el BCL? ¿Alguna falla con ValueWrapper<T> propuesta arriba?

(Mi uso real es más complicado que el ejemplo anterior por lo que el manejo de la variable dentro de mi foreach bucle que llama Foo() no es una opción. Período.)

Respuesta

4

No, estoy bastante seguro de que no hay nada existente en el BCL que puede hacer esto. Tu mejor opción es precisamente lo que has propuesto, creo. La implementación de ValueWrapper realmente no necesita ser más complicada que lo que ha propuesto.

Por supuesto, no se garantiza que sea seguro para subprocesos, pero si lo necesita, puede simplemente convertir la propiedad automática en una estándar con una variable de respaldo y marcar el campo como volatile (para asegurar que el valor esté arriba) hasta la fecha en todo momento).

+2

Hacer que un campo sea volátil no es suficiente para garantizar la seguridad de las cadenas de caracteres, ya que las escrituras en tipos de valores arbitrarios no se garantiza que sean atómicas según la especificación C#. Volátil no garantiza la atomicidad, simplemente elimina algunos problemas de ordenamiento inducidos por la optimización del compilador. –

+0

Si le importa la seguridad del hilo, use bloqueos. –

+0

@Eric: Sí, buen punto. Originalmente escribí que garantiza la atomicidad, pero luego la eliminé rápidamente ya que me di cuenta de que esto no era necesariamente así. – Noldorin

5

Si sólo necesita escribir el valor a continuación, otra técnica sería:

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

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

Coincidentemente, que voy a hacer una serie sobre las razones por las que hay tantas restricciones goofy sobre bloques de iterador en mi blog en julio. "¿Por qué no hay parámetros de ref?" será temprano en la serie.

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

0

mucho tiempo he pensado que el BCL realmente debe tener una clase y una interfaz algo como lo siguiente:

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

Aunque muchas personas instintivamente envuelven campos de auto-propiedades, campos menudo permita que el limpiador y un código más eficiente especialmente cuando se usan tipos de valores. En muchas situaciones, la mayor encapsulación que se puede obtener mediante el uso de propiedades puede valer la pena en eficiencia y semántica, pero cuando el propósito de un tipo es ser un objeto de clase cuyo estado está completamente expuesto y mutable, dicha encapsulación es contraproducente.

Se incluye la interfaz no debido a que muchos usuarios de una MutableWrapper<T> se desea utilizar la interfaz en su lugar, sino más bien porque un IReadWriteActUpon<T> podría ser útil en una variedad de situaciones, algunas de las cuales implicarían la encapsulación, y alguien que tiene una instancia de MutableWrapper<T> podría pasarlo al código que está diseñado para trabajar con datos encapsulados en una interfaz IReadWriteActUpon<T>.

Cuestiones relacionadas