我正在编写一个需要传递可变整数的迭代器。
public IEnumerable<T> Foo(ref int valueThatMeansSomething)
{
// Stuff
yield return ...;
}
这让我知道“错误476迭代器不能有参考或输出参数”。
我需要的是在迭代器中修改这个整数值,并由迭代器的调用者使用。换句话说,无论上面的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
的{{1}}循环中的变量不是一个选项。期间。)
答案 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>
接口中的数据。