.NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 )...
如果你使用過 WPF/UWP 等 XAML UI 框架,那么應該了解到附加屬性的概念。那么沒有依賴屬性支持的時候如何做附加屬性的功能呢?你可能會想到弱引用。但這需要做一個弱引用字典,要寫的代碼還是非常麻煩的。
本文介紹 .NET 的?ConditionalWeakTable<TKey,TValue>?類型,適用于 .NET Framework 4.0 以上和全部 .NET Core 的版本。
這不是字典
現成可用的弱引用字典,即?ConditionalWeakTable<TKey,TValue>。然而實際上這個類的原本作用并不是當作字典使用!
如果你使用過 WPF/UWP 等 XAML UI 框架,那么應該了解到附加屬性的概念。這其實是 .NET 為我們提供的一種附加字段的機制。
比如你有一個類:
class Foo{
// 請忽略這里公有字段帶來的設計問題,只是為了演示。
public string A;
}
我們希望為它增加一個字段?Bar:
class Foo{
public string A;
public Bar Bar;
}
那么我們需要修改類?Foo?本身以實現這個效果;但是這樣就使得?Foo?耦合了?Bar,從而破壞了內聚性/依賴倒置原則。典型的情況是?Foo?類表示一個人?Person,它里面不應該包含一個?某行賬號?這樣的字段,因為很多人是沒有那家銀行賬號的。這個信息讓那家銀行存起來才是比較符合設計原則的設計。
我們可以通過一個字典?Dictionary<Foo, Bar>?來存儲所有?Foo?實例額外增加的?Bar?的值可以避免讓?Foo?類中增加?Bar?字段從而獲得更好的設計。但這樣就引入了一個靜態字典從而使得所有的?Foo?和?Bar?的實例無法得到釋放。我們想當然希望擁有一個弱引用字典來解決問題。然而這是一個?X-Y 問題。
實際上 .NET 中提供了?ConditionalWeakTable<TKey,TValue>?幫我們解決了最本質的問題——在部分場景下期望為?Foo?類添加一個字段。雖然它不是弱引用字典,但能解決此類問題,同時也能當作一個弱引用字典來使用,僅此而已。
你需要注意的是,ConditionalWeakTable<TKey,TValue>?并不實現?IDictionary<TKey,TValue>?接口,只是里面有一些像?IDictionary<TKey, TValue>?的方法,可以當作字典使用,也可以遍歷取出剩下的所有值。
驗證
ConditionalWeakTable<TKey,TValue>?中的所有 Key 和所有的 Value 都是弱引用的,并且會在其 Key 被回收或者 Key 和 Value 都被回收之后自動從集合中消失。這意味著當你使用它來為一個類型附加一些字段或者屬性的時候完全不用擔心內存泄漏的問題。
下面我寫了一段代碼用于驗證其內存泄漏問題:
向?ConditionalWeakTable<TKey,TValue>?中添加了三個鍵值對;
將后兩個的?key?設為?null;
進行垃圾回收。
using System.Linq;
using System.Runtime.CompilerServices;
namespace Walterlv.Demo.Weak
{
class Program
{
public static void Main()
{
var key1 = new Key("Key1");
var key2 = new Key("Key2");
var key3 = new Key("Key3");
var table = new ConditionalWeakTable<Key, WalterlvValue>
{
{key1, new WalterlvValue()},
{key2, new WalterlvValue()},
{key3, new WalterlvValue()}
};
var weak2 = new WeakReference(key2);
key2 = null;
key3 = null;
GC.Collect();
Console.WriteLine($@"key1 = {key1?.ToString() ?? "null"}key2 = {key2?.ToString() ?? "null"}, weak2 = {weak2.Target ?? "null"}key3 = {key3?.ToString() ?? "null"}Table = {{{string.Join(", ", table.Select(x => $"{x.Key} = {x.Value}"))}}}");
}
}
public class Key
{
private readonly string _name;
public Key(string name) => _name = name;
public override string ToString() => _name;
}
public class WalterlvValue
{
public DateTime CreationTime = DateTime.Now;
public override string ToString() => CreationTime.ToShortTimeString();
}
}
這段代碼的運行結果如下圖:
從中我們可以發現:
當某個 Key 被回收后,ConditionalWeakTable<TKey,TValue>?中就沒有那一項鍵值對了;
當 Key 的實例依然在的時候,ConditionalWeakTable<TKey,TValue>?中的 Value 依然還會存在。
另外,我們這里在調查內存泄漏問題,你需要在 Release 配置下執行此代碼才能得到最符合預期的結果。
參考資料
ConditionalWeakTable<TKey,TValue> Class (System.Runtime.CompilerServices) - Microsoft Docs
Good implementation of weak dictionary in .Net - Stack Overflow
Presenting WeakDictionary[TKey, TValue] – Nick Guerrera’s blog
.net - Understanding ConditionalWeakTable - Stack Overflow
原文地址:https://blog.walterlv.com/post/conditional-weak-table.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的.NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 )...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用.NET Core 编写端到端测试
- 下一篇: 在C#中使用Json.Net进行序列化和