可变的值类型包装器传递给迭代器

时间:2009-06-26 18:01:45

标签: c# .net-3.5 iterator value-type reference-type

我正在编写一个需要传递可变整数的迭代器。

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

    yield return ...;
}

这让我知道“错误476迭代器不能有参考或输出参数”。

我需要的是在迭代器中修改这个整数值,并由迭代器的调用者使用。换句话说,无论上面的Foo()调用想要知道valueThatMeansSomethingFoo()的结束值,都可以使用它本身。真的,我想要一个引用类型的整数而不是值类型。

我唯一能想到的就是写一个封装了我的整数的类,允许我修改它。

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的{​​{1}}循环中的变量不是一个选项。期间。)

3 个答案:

答案 0 :(得分:5)

如果您只需要写入值,那么另一种技术将是:

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

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

巧合的是,我将在7月份的博客中对迭代器块有如此多的愚蠢限制的原因进行系列研究。 “为什么没有参考参数?”将在系列的早期。

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

答案 1 :(得分:4)

不,我非常有信心BCL中没有任何东西可以做到这一点。你最好的选择正是你提出的我想的。 ValueWrapper的实施确实不需要比你提议的更复杂。

当然,它不能保证是线程安全的,但如果你需要,你可以简单地将自动属性转换为带有支持变量的标准属性,并将字段标记为volatile(以确保值在任何时候都是最新的。)

答案 2 :(得分:0)

我一直认为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>接口中的数据。