WeakRefDictionary 类是一个弱引用字典,在该类中 Microsoft 为我们示范了 .NET 中弱引用的用法,希望大家在看这个类时能够注意到这一点。
另外,在 ObjectBuilder 中,默认实现的“定位器”也是使用该类作为“底层存储”,它最主要的特点就是不会阻止 CLR 回收该字典中的对象(只要指向该对象的强引用变量的数量为 0)。
1
using System;
2
using System.Collections.Generic;
3
using Microsoft.Practices.ObjectBuilder.Properties;
4![](/Images/OutliningIndicators/None.gif)
5
namespace Microsoft.Practices.ObjectBuilder
6![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
7![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
8
/// 表示使用 "弱引用"
9
/// 保存 "键/值" 中的
10
/// 值的字典。该类支
11
/// 持空引用。
12
/// </summary>
13
/// <remarks>
14
/// <para>
15
/// 该类是一个 "弱引
16
/// 用" 字典类。添加
17
/// 到该类中的对象不
18
/// 会因为该字典中保
19
/// 存了指向该对象的
20
/// 引用变量而不被垃
21
/// 圾收集,换句话说:
22
/// 即使某个对象已经
23
/// 被添加到该字典中,
24
/// CLR 仍然会对该对
25
/// 象执行垃圾收集(
26
/// 如果有其它引用指
27
/// 向该对象,不能被
28
/// 垃圾收集)。
29
/// </para>
30
/// <para>
31
/// 该类是对 <see cref="Dictionary{TKey,TValue}"/>
32
/// 的一个封装,并且
33
/// 它们的使用方式也
34
/// 是一样的,唯一的
35
/// 区别是保存在该字
36
/// 典中的对象会被执
37
/// 行垃圾收集并被清
38
/// 理出字典。下面是
39
/// 该类执行自动清理
40
/// 的时机:
41
///
42
/// 1、获取 Count属性值
43
/// 时。
44
///
45
/// 2、调用 TryGet 函数
46
/// 获取指定键的对象
47
/// 时。
48
/// </para>
49
/// <para>
50
/// "弱引用" 的使用
51
/// 技巧:
52
///
53
/// 1、要操作一个 "弱引
54
/// 用" 指向的对象,
55
/// 应用程序须获取该
56
/// 对象的一个 "强引
57
/// 用" ,然后使用这
58
/// 个 "强引用" 变量
59
/// 操作对象。
60
///
61
/// 2、一旦创建了一个对
62
/// 象的"弱引用",开
63
/// 发人员通常需要将
64
/// 该对象的 "强引用"
65
/// 设置为 null ,否
66
/// 则 CLR将不会对该
67
/// 对象执行垃圾收集。
68
///
69
/// 3、不要直接使用 "弱
70
/// 引用" 的 Target
71
/// 属性操作对象,CLR
72
/// 无法保证两次调用
73
/// Target 属性期间
74
/// 不进行垃圾收集。
75
/// 而一般情况下开发
76
/// 人员希望不执行垃
77
/// 圾收集。
78
/// </para>
79
/// </remarks>
80
/// <typeparam name="TKey">
81
/// 键的类型。
82
/// </typeparam>
83
/// <typeparam name="TValue">
84
/// 值的类型。
85
/// </typeparam>
86
public class WeakRefDictionary<TKey, TValue>
87![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
88![](/Images/OutliningIndicators/InBlock.gif)
89![](/Images/OutliningIndicators/ContractedSubBlock.gif)
字段#region 字段
90![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
91
/// 内部使用的存储结
92
/// 构,是一个"键/弱
93
/// 引用" 对的字典。
94
/// </summary>
95
private Dictionary<TKey, WeakReference> inner = new Dictionary<TKey, WeakReference>();
96
#endregion
97![](/Images/OutliningIndicators/InBlock.gif)
98![](/Images/OutliningIndicators/ContractedSubBlock.gif)
属性#region 属性
99![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
100
/// 获取包含在字典中
101
/// 的项的数目。
102
/// </summary>
103
/// <remarks>
104
/// 由于使用 "弱引用"
105
/// 保存该字典中的项,
106
/// 因此不能保证该属
107
/// 性值与通过枚举得
108
/// 到的项的数量相同。
109
/// 应该把 Count 当
110
/// 作一个估算值。
111
/// </remarks>
112
public int Count
113![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
114
get
115![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
116
CleanAbandonedItems();
117
return inner.Count;
118
}
119
}
120![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
121
/// 检索字典中的值。
122
/// </summary>
123
/// <param name="key">要查找的键。</param>
124
/// <returns>字典中的值。</returns>
125
/// <exception cref="KeyNotFoundException">
126
/// 当字典中不存在 key
127
/// 时引发。由于该字
128
/// 典中包含的是 "弱
129
/// 引用",因此该 key
130
/// 可能由于对象被垃
131
/// 圾回收而被清理。
132
/// </exception>
133
public TValue this[TKey key]
134![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
135
get
136![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
137
TValue result;
138
bool succeed = TryGet(key, out result);
139![](/Images/OutliningIndicators/InBlock.gif)
140
if (!succeed)
141![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
142
throw new KeyNotFoundException();
143
}
144![](/Images/OutliningIndicators/InBlock.gif)
145
return result;
146
}
147
}
148
#endregion
149![](/Images/OutliningIndicators/InBlock.gif)
150![](/Images/OutliningIndicators/ContractedSubBlock.gif)
私有方法#region 私有方法
151![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <remarks>
152
/// 对 "弱引用" 指向
153
/// 的对象进行编码。
154
/// </remarks>
155
private object EncodeNullObject(object value)
156![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
157
if (value == null)
158![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
159
return typeof(NullObject);
160
}
161![](/Images/OutliningIndicators/InBlock.gif)
162
return value;
163
}
164![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
165
/// 对 "弱引用" 指向
166
/// 的对象进行解码。
167
/// </summary>
168
private TObject DecodeNullObject<TObject>(object innerValue)
169![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
170![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
171
* 在应用程序运行时
172
* 候,CLR 会为程序
173
* 中用到的每个类型
174
* 创建一个内部的数
175
* 据结构和 Type 对
176
* 象,在整个程序运
177
* 行期间它们都不会
178
* 被销毁。我们在程
179
* 序中使用 object.GetType();
180
* 等方法获取的都是
181
* 指向相同 Type 对
182
* 象的引用变量(对
183
* 同一个类型而言)。
184
*/
185
if (innerValue == typeof(NullObject))
186![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
187
return default(TObject);
188
}
189![](/Images/OutliningIndicators/InBlock.gif)
190
return (TObject)innerValue;
191
}
192![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
193
/// 清理字典中被垃圾
194
/// 回收的项。
195
/// </summary>
196
/// <remarks>
197
/// 当 "弱引用" 指向
198
/// 的对象为空引用时,
199
/// 则表示该对象已经
200
/// 被垃圾回收。这面
201
/// 里有一个问题:如
202
/// 何让 "弱引用" 指
203
/// 向一个空引用?这
204
/// 里通过对 "弱引用"
205
/// 指向的对象进行编
206
/// 码和解码来解决这
207
/// 个问题。
208
/// </remarks>
209
private void CleanAbandonedItems()
210![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
211
List<TKey> deadKeys = new List<TKey>();
212![](/Images/OutliningIndicators/InBlock.gif)
213
foreach (KeyValuePair<TKey, WeakReference> kvp in inner)
214![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
215
if (kvp.Value.Target == null)
216![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
217
deadKeys.Add(kvp.Key);
218
}
219
}
220![](/Images/OutliningIndicators/InBlock.gif)
221
foreach (TKey key in deadKeys)
222![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
223
inner.Remove(key);
224
}
225
}
226
#endregion
227![](/Images/OutliningIndicators/InBlock.gif)
228![](/Images/OutliningIndicators/ContractedSubBlock.gif)
公有方法#region 公有方法
229![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
230
/// 将指定的键和值添
231
/// 加到字典中。
232
/// </summary>
233
/// <param name="key">
234
/// 要添加的键。
235
/// </param>
236
/// <param name="value">
237
/// 要添加的值。
238
/// </param>
239
public void Add(
240
TKey key,
241
TValue value)
242![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
243
TValue dummy;
244![](/Images/OutliningIndicators/InBlock.gif)
245
if (TryGet(key, out dummy))
246![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
247
throw new ArgumentException(Resources.KeyAlreadyPresentInDictionary);
248
}
249![](/Images/OutliningIndicators/InBlock.gif)
250![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
251
* 在该类里,由于指
252
* 向空引用的 "弱引
253
* 用" 对象被认为是
254
* 指向一个已经被垃
255
* 圾回收的对象,因
256
* 此这里不能使用空
257
* 引用创建 "弱引用"
258
* 对象。在这儿,通
259
* 过调用 EncodeNullObject
260
* 函数将空引用转换
261
* 为一个指向内部类
262
* (NullObject 类)
263
* 的类型对象的引用
264
* 并使用该引用创建
265
* 一个 "弱引用" 对
266
* 象来避免这种情况。
267
*/
268
inner.Add(
269
key,
270
new WeakReference(EncodeNullObject(value)));
271
}
272![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
273
/// 确定字典中是否包
274
/// 含指定键的值。
275
/// </summary>
276
/// <param name="key">
277
/// 要在字典中定位的
278
/// 键。
279
/// </param>
280
/// <returns>
281
/// 如果字典中包含指
282
/// 定键的项,则为 true;
283
/// 否则为 false。
284
/// </returns>
285
public bool ContainsKey(TKey key)
286![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
287
TValue dummy;
288
return TryGet(key, out dummy);
289
}
290![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
291
/// 尝试获取与指定键
292
/// 相关联的值。
293
/// </summary>
294
/// <param name="key">
295
/// 要获取值的键。
296
/// </param>
297
/// <param name="value">
298
/// 如果找到该键,则
299
/// 该输出参数的值为
300
/// 与指定键相关联的
301
/// 值;否则,则返回
302
/// value 参数类型的
303
/// 默认值。
304
/// </param>
305
/// <returns>
306
/// 如果字典中包含指
307
/// 定键的项,则为 true;
308
/// 否则为 false。
309
/// </returns>
310
/// <remarks>
311
/// 如果与指定键相关
312
/// 联的对象已经被垃
313
/// 圾回收,则从字典
314
/// 中清理该项并返回
315
/// false(表示该操
316
/// 作执行失败)。
317
/// </remarks>
318
public bool TryGet(
319
TKey key,
320
out TValue value)
321![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
322
value = default(TValue);
323![](/Images/OutliningIndicators/InBlock.gif)
324
WeakReference wr;
325
if (!inner.TryGetValue(key, out wr))
326![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
327
return false;
328
}
329![](/Images/OutliningIndicators/InBlock.gif)
330![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
331
* 如果走到这里,可
332
* 以确保字典中包含
333
* 指定键的项。这里
334
* 将其保存在 result
335
* 引用中可以确保在
336
* 该函数调用完成之
337
* 前 CLR 不对该对
338
* 象执行垃圾回收。
339
*/
340
object result = wr.Target;
341![](/Images/OutliningIndicators/InBlock.gif)
342![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
343
* 如果 result 为空
344
* 引用,则表示 result
345
* 指向的对象已经被
346
* 垃圾回收。
347
*/
348
if (result == null)
349![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
350![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
351
* 移除已经被垃圾回
352
* 收的项。
353
*/
354
inner.Remove(key);
355
return false;
356
}
357![](/Images/OutliningIndicators/InBlock.gif)
358![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
359
* 对于指向 NullObject
360
* 类型对象的引用,
361
* 应该把它还原为空
362
* 引用。
363
*/
364
value = DecodeNullObject<TValue>(result);
365
return true;
366
}
367![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
368
/// 从字典中移除指定
369
/// 键的项。
370
/// </summary>
371
/// <param name="key">
372
/// 要移除项的键。
373
/// </param>
374
/// <returns>
375
/// 如果成功移除该项,
376
/// 则为 true; 否则
377
/// 为 false。
378
/// </returns>
379
public bool Remove(TKey key)
380![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
381
return inner.Remove(key);
382
}
383![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
384
/// 返回循环访问字典
385
/// 的枚举数。
386
/// </summary>
387
/// <returns>枚举数。</returns>
388
/// <remarks>
389
/// 由于字典中的对象
390
/// 可以从枚举数返回
391
/// 并被使用,因此这
392
/// 里会使用一个临时
393
/// 的强引用变量指向
394
/// 该对象来阻止在执
395
/// 行枚举期间的垃圾
396
/// 回收。注意:一旦
397
/// 枚举结束,那些临
398
/// 时的强引用将被释
399
/// 放。如果想继续阻
400
/// 止垃圾回收,则必
401
/// 须在枚举期间使用
402
/// 强引用变量指向这
403
/// 些对象。
404
/// </remarks>
405
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
406![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
407
foreach (KeyValuePair<TKey, WeakReference> kvp in inner)
408![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
409![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//*
410
* 使用 innerValue
411
* 指向对象可以阻止
412
* CLR 执行垃圾回收。
413
* 这样可以确保在创
414
* 建 KeyValuePair
415
* 对象之前, "弱引
416
* 用" 指向的对象不
417
* 被垃圾回收。
418
*/
419
object innerValue = kvp.Value.Target;
420![](/Images/OutliningIndicators/InBlock.gif)
421
if (innerValue != null)
422![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
423
yield return new KeyValuePair<TKey, TValue>(kvp.Key, DecodeNullObject<TValue>(innerValue));
424
}
425
}
426
}
427
#endregion
428![](/Images/OutliningIndicators/InBlock.gif)
429![](/Images/OutliningIndicators/ContractedSubBlock.gif)
NullObject 类#region NullObject 类
430![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
431
/// 内部类,该类只能
432
/// 由 "弱引用字典"
433
/// 类使用, "弱引用
434
/// 字典" 类使用该类
435
/// 的类型对象来创建
436
/// 表示空引用的 "弱
437
/// 引用" 对象。
438
/// </summary>
439
private class NullObject
440![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{}
441
#endregion
442![](/Images/OutliningIndicators/InBlock.gif)
443
}
444
}