.net 语言中结构类型的主要限制之一是,虽然可写结构类型存储位置的字段(变量、字段、参数、数组槽等)本身是可写存储位置(如果它们是结构类型,它们的字段同样是可写的存储位置),这种访问只适用于结构类型的存储位置System.Array
,并且除了可以公开像存储位置一样工作的属性之外,没有任何其他类型可以通过它来公开。
如果一个类提供了一种方法,该方法可以将该存储位置作为ref
参数传递给提供的回调,则类可以将私有存储位置公开为受控情况下的存储位置。
例如:
public delegate void ActByRef<T1>(ref T1 p1);
public delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);
public delegate void ActByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, ref T3 p3);
public void ActOnSize(ActByRef proc)
{ proc( ref _someSize); }
public void ActOnSize<XT1>(ActByRef proc, ref XT1 xp1)
{ proc( ref _someSize, ref xp1); }
public void ActOnSize<XT1,XT2>(ActByRef proc, ref XT1 xp1, ref XT2 xp2)
{ proc( ref _someSize, ref xp1, ref xp2); }
如果希望在暴露这种方法的某物的宽度上增加 5,则可以使用
thing.ActOnSize((ref Size sz) => sz.Width += 5);
如果有一个局部变量“HeightAdder”,并且希望将其添加到对象的高度,可以使用
thing.ActOnSize((ref Size sz, ref int adder) =>
sz.Height += adder, ref HeightAdder);
请注意,由于编写的 lambda 表达式不会捕获任何局部变量,因此可以将其评估为静态委托(如果HeightAdder
委托已将范围,并且每次执行方法调用时都需要生成一个委托;将数量作为参数传递可以避免这种开销)。ref
adder
HeightAdder
ref
如果访问方法包括索引或键的参数,则类似的方法可以由类似于List<T>
或Dictionary<TKey,TValue>
允许回调方法直接作用于列表槽或字典条目的类使用。
这种方法的一个很好的特点是它允许集合提供并发访问的样式,否则这些访问是很困难的。如果集合中的事物属于可与Interlocked
方法一起使用的类型(或者是其字段可与此类方法一起使用的公开结构类型),则客户端代码可以使用此类方法对底层数据执行线程安全的原子操作。如果集合中的东西不是这种类型,那么集合可能能够比其他方法更安全地实现锁定。例如, aConcurrentIndexedSet<T>
可能有一个ActOnItem(int item, int timeout, ActByRef<T> proc)
; 将在锁内的项目上调用的方法proc
(相信客户端提供proc
可以信任在合理的时间范围内返回)。虽然这样的代码无法防止出现proc
死锁或以其他方式被搁置的可能性,但它可以确保在将控制权返回给调用代码之前释放锁。