Pergunta

Eu estou escrevendo um iterador que precisa passar em torno de um número inteiro mutável.

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

    yield return ...;
}

Este redes me "Erro 476 Iterators não pode ter ref ou out parâmetros".

O que eu preciso é este valor inteiro a ser modificado no iterador e utilizável pelo chamador do iterador. Em outras palavras, tudo o que chama Foo() acima quer saber o valor final de valueThatMeansSomething e Foo() pode usá-lo em si. Realmente, eu quero um número inteiro que é um tipo de referência não um tipo de valor.

A única coisa que posso pensar é escrever uma classe que encapsula o meu inteiro e me permite modificá-lo.

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

    public T Item { get; set; }
}

Assim:

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

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

Existe alguma classe ou mecanismo para lidar com isso já no BCL? Qualquer falhas com ValueWrapper<T> propostas acima?

(My uso real é mais complicado do que o exemplo acima para lidar com a variável dentro de meu loop foreach que as chamadas Foo() não é uma opção. Período.)

Foi útil?

Solução

Não, eu estou bastante confiante que não há nada existente na BCL que pode fazer isso. Sua melhor opção é exatamente o que você propuseram que eu penso. A implementação de ValueWrapper realmente não precisa ser mais complicado do que o que você propôs.

É claro, não é garantido para ser thread-safe, mas se você precisa que você pode simplesmente converter a propriedade automática em um padrão com uma variável de apoio e marcar o campo como volatile (para segurar o valor é up-to-date em todos os tempos).

Outras dicas

Se você só precisa escrever o valor, em seguida, uma outra técnica seria:

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

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

Por coincidência, eu vou estar fazendo uma série sobre as razões pelas quais há tantas restrições pateta em iteradoras blocos no meu blog em julho. "Por que há parâmetros ref?" será no início da série.

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

Há muito tempo pensei que a BCL realmente deve ter uma classe e interface de algo como o seguinte:

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

Apesar de muitas pessoas instintivamente embrulhar campos em auto-propriedades, campos, muitas vezes permitem um código mais limpo e mais eficiente, especialmente quando usando tipos de valor. Em muitas situações, o aumento de um encapsulamento pode ganhar usando propriedades pode valer a pena o custo em eficiente e semântica, mas quando todo o propósito de um tipo é ser um objeto de classe cujo estado é completamente exposta e mutável, como o encapsulamento é contraproducente.

A interface está incluído não porque muitos usuários de um MutableWrapper<T> iria querer usar a interface em vez disso, mas sim porque um IReadWriteActUpon<T> poderia ser útil em uma variedade de situações, algumas das quais implicaria encapsulamento, e alguém que tem uma instância de MutableWrapper<T> pode querer passá-lo para código que é projetado para trabalhar com dados encapsulados em uma interface IReadWriteActUpon<T>.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top