【问题标题】:Mutable wrapper of value types to pass into iterators传递给迭代器的值类型的可变包装器
【发布时间】:2010-11-06 05:31:54
【问题描述】:

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

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&lt;T&gt; 有任何缺陷吗?

(我的实际使用比上面的例子更复杂,所以处理我的foreach循环中调用Foo()的变量不是一个选项。句号。)

【问题讨论】:

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


    【解决方案1】:

    我一直认为 BCL 真的应该有一个类和接口,如下所示:

    公共代表 void ActByRef(ref T1 p1); 公共代表 void ActByRefRef(ref T1 p1, ref T2 p2); 公共接口 IReadWriteActUpon { T值{get;放;} void ActUpon(ActByRef proc); void ActUpon(ActByRefRef proc, 参考 TExtraparam ExtraParam); } 公共密封类 MutableWrapper : IReadWrite { 公共 T 值; public MutableWrapper(T value) { this.Value = value; } T IReadWriteActUpon.Value {get {return this.Value;} set {this.Value = value;} } 公共无效ActUpon(ActByRef proc) { 过程(参考值); } 公共无效 ActUpon(ActByRefRef proc, 参考 TExtraparam ExtraParam) { proc(参考值,参考 ExtraParam); } }

    尽管许多人本能地将字段包装在自动属性中,但字段通常允许更简洁、更高效的代码,尤其是在使用值类型时。在许多情况下,通过使用属性获得的更多封装可能值得在效率和语义方面付出代价,但是当一个类型的全部目的是成为一个状态完全公开和可变的类对象时,这种封装适得其反。

    包含该接口并不是因为MutableWrapper&lt;T&gt; 的许多用户想要使用该接口,而是因为IReadWriteActUpon&lt;T&gt; 可能在各种情况下有用,其中一些需要封装,并且有人有一个MutableWrapper&lt;T&gt; 的实例可能希望将其传递给旨在处理封装在IReadWriteActUpon&lt;T&gt; 接口中的数据的代码。

    【讨论】:

      【解决方案2】:

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

      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

      【讨论】:

        【解决方案3】:

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

        当然,不能保证它是线程安全的,但是如果您需要,您可以简单地将自动属性转换为带有支持变量的标准属性并将该字段标记为volatile(以确保值向上-to-date)。

        【讨论】:

        • 使字段 volatile 不足以确保线程安全,因为 C# 规范不保证对任意值类型的写入是原子的。 Volatile 不保证原子性,它只是消除了一些编译器优化引起的排序问题。
        • @Eric:是的,好点子。我最初写的是它保证原子性,但后来我意识到不一定是这种情况,所以很快就把它删除了。
        • 澄清一下:我认为正确的方法是将字段声明为易失性使用锁?
        • 如果对数据的每次访问都正确地处于锁定状态,那么在您查看数据时它不可能更改。那么为什么它需要是易变的呢?
        • 仅供参考,CLI 规范和 CLR 实现的内容有点不同; CLR 2.0 及更高版本实现了比 CLI 规范要求的更强大的内存模型。特别是,在没有 volatile 的 CLR 2.0 中,双重检查的单例初始化保证是正确的,但 CLI 不能。如果你真的对这些东西感兴趣,试试 Joe Duffy 的博客;他是这方面最好的专家。
        猜你喜欢
        • 2021-08-22
        • 2016-07-24
        • 2013-06-03
        • 1970-01-01
        • 2021-12-11
        • 2019-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多