Pregunta

Estoy escribiendo un iterador que necesita pasar un número entero mutable.

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

    yield return ...;
}

Esto me genera el mensaje "Error 476 Los iteradores no pueden tener parámetros de referencia o de salida".

Lo que necesito es que este valor entero se modifique en el iterador y pueda ser utilizado por la persona que llama al iterador.En otras palabras, cualquier cosa que llame Foo() arriba quiere saber el valor final de valueThatMeansSomething y Foo() puede utilizarlo por sí mismo.Realmente, quiero un número 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; }
}

Entonces:

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

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

¿Existe alguna clase o mecanismo para manejar esto ya en el BCL? Cualquier defecto con ValueWrapper<T> propuesto anteriormente?

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

¿Fue útil?

Solución

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

Por supuesto, no se garantiza que sea seguro para subprocesos, pero si lo necesita, simplemente puede 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é actualizado en todo momento).

Otros consejos

Si solo necesita escribir el valor, otra técnica sería:

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

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

Casualmente, haré una serie sobre las razones por las que hay tantas restricciones tontas en los bloques iteradores en mi blog en julio. " ¿Por qué no hay parámetros de referencia? " será temprano en la serie.

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

Durante mucho tiempo pensé que el BCL realmente debería tener una clase e interfaz similar a la 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 envuelven instintivamente los campos en propiedades automáticas, los campos a menudo permiten un código más limpio y eficiente, especialmente cuando se usan tipos de valor. En muchas situaciones, el aumento de la 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 completo de un tipo es ser un objeto de clase cuyo estado esté completamente expuesto y mutable, dicha encapsulación es contraproducente.

La interfaz se incluye no porque muchos usuarios de un MutableWrapper<T> quieran usar la interfaz, sino porque un IReadWriteActUpon<T> podría ser útil en una variedad de situaciones, algunas de las cuales implicarían encapsulación, y alguien que tiene una instancia de <=> podría desear pasarlo a un código diseñado para trabajar con datos encapsulados en una interfaz <=>.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top