代码之家  ›  专栏  ›  技术社区  ›  Colin Burnett

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

  •  5
  • Colin Burnett  · 技术社区  · 17 年前

    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() 这不是一种选择。句号。)

    3 回复  |  直到 17 年前
        1
  •  5
  •   sehe    14 年前

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

    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

        2
  •  4
  •   Noldorin    17 年前

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

    volatile (确保价值始终是最新的)。

        3
  •  0
  •   supercat    13 年前

    我一直认为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> 可能在各种情况下都很有用,其中有些情况需要封装,有些人有 可变包装<T> 可能希望将其传递给设计用于处理封装在 iReadWriteAtupon<T>