我正在写一个需要绕过一个可变的整数的迭代器。
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
我一直认为,首创置业真的应该有一个类和接口类似如下:
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>
接口。