Изменяемая оболочка типов значений для передачи в итераторы

StackOverflow https://stackoverflow.com/questions/1050413

Вопрос

Я пишу итератор, который должен передавать изменяемое целое число.

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

    yield return ...;
}

Это приводит меня к выводу: «Ошибка 476. Итераторы не могут иметь параметры ref или out».

Мне нужно, чтобы это целочисленное значение было изменено в итераторе и могло использоваться вызывающей стороной итератора.Другими словами, все, что вызывает Foo() выше хочет знать конечное значение valueThatMeansSomething и Foo() может использовать его сам.На самом деле, мне нужно целое число, которое является ссылочным типом, а не типом значения.

Единственное, о чем я могу думать, это написать класс, который инкапсулирует мое целое число и позволяет мне его изменять.

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

    public T Item { get; set; }
}

Так:

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

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

Есть ли какой-либо класс или механизм для решения этой проблемы в BCL? Любые недостатки с ValueWrapper<T> предложено выше?

(Мое фактическое использование более сложное, чем в приведенном выше примере, поэтому обработка переменной внутри моего foreach цикл, который вызывает Foo() это не вариант.Период.)

Это было полезно?

Решение

Нет, я вполне уверен, что в BCL нет ничего, что могло бы это сделать.Я думаю, что лучший вариант — это именно то, что вы предложили.Реализация ValueWrapper на самом деле не обязательно должно быть сложнее, чем то, что вы предложили.

Конечно, потокобезопасность не гарантируется, но если вам это нужно, вы можете просто преобразовать автоматическое свойство в стандартное с резервной переменной и пометить поле как volatile (чтобы гарантировать, что значение всегда актуально).

Другие советы

Если вам нужно только написать значение, тогда можно использовать другой метод:

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

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

По совпадению, в июле в моем блоге я напишу серию статей о причинах, по которым существует так много глупых ограничений на блоки итераторов."Почему нет параметров Ref?" Будет в начале серии.

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

Я давно думал, что BCL действительно должен иметь класс и интерфейс примерно следующего содержания:

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

Хотя многие люди инстинктивно оборачивают поля автоматическими свойствами, поля часто позволяют создавать более чистый и эффективный код, особенно при использовании типов значений.Во многих ситуациях улучшенная инкапсуляция, которую можно получить за счет использования свойств, может оправдать затраты с точки зрения эффективности и семантики, но когда вся цель типа — быть объектом класса, состояние которого полностью открыто и изменчиво, такая инкапсуляция контрпродуктивна.

Интерфейс включен не потому, что многие пользователи MutableWrapper<T> вместо этого хотелось бы использовать интерфейс, а скорее потому, что IReadWriteActUpon<T> может быть полезно в различных ситуациях, некоторые из которых повлекут за собой инкапсуляцию, и тот, у кого есть экземпляр MutableWrapper<T> может захотеть передать его в код, предназначенный для работы с данными, инкапсулированными в IReadWriteActUpon<T> интерфейс.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top