1.5.2 Wrapper Wrapper 模式是一種常見的設計模式,它可以用來包裝現有的物件,提供額外的功能或改變其行為。在泛型的幫助下,我們可以建立通用的 Wrapper 類別來處理任何型別的物件
🎯 Wrapper 基本概念 Wrapper 類別的主要目的是:
🔐 封裝原始物件 :提供對底層物件的受控存取
🎨 增強功能 :在不修改原始類別的情況下添加新功能
🛡️ 保護資料 :控制對包裝物件的存取方式
🔄 轉換介面 :為不相容的介面提供適配
📝 實際應用範例 以下範例展示了泛型 Wrapper 的使用和一些需要注意的問題:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 void Main (){ var items = new List<int > { 1 , 2 , 3 }; var wrappers = CreateWrapper2<int >(items); var store = new List<Wrapper<int >>(); store.AddRange(wrappers); var storeContainsAnyWrappers = wrappers .Any(wrapper => store.Contains(wrapper)).Dump(); } public IEnumerable <Wrapper <T >> CreateWrapper <T >(IEnumerable<T> items ){ return items.Select(item => new Wrapper<T>(item)); } public IEnumerable <Wrapper <T >> CreateWrapper2 <T >(IEnumerable<T> items ){ return items.Select(item => { Console.WriteLine($"Create wrapper for {item} " ); return new Wrapper<T>(item); }).ToList(); } public class Wrapper <T >{ private readonly T _item; public Wrapper (T item ) { _item = item; } }
🔍 問題分析 在上述範例中,storeContainsAnyWrappers 會回傳 false,這是因為:
🚨 物件參考比較問題:
CreateWrapper2 建立了新的 Wrapper<T> 實例
store.AddRange(wrappers) 將這些實例加入到 store 中
Contains() 使用預設的參考相等性比較
即使包裝的值相同,但 Wrapper 物件是不同的實例
✅ 解決方案 方案一:實作 IEquatable 介面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Wrapper <T > : IEquatable <Wrapper <T >>{ private readonly T _item; public Wrapper (T item ) { _item = item; } public T Item => _item; public bool Equals (Wrapper<T> other ) { if (other is null ) return false ; if (ReferenceEquals(this , other)) return true ; return EqualityComparer<T>.Default.Equals(_item, other._item); } public override bool Equals (object obj ) { return obj is Wrapper<T> wrapper && Equals(wrapper); } public override int GetHashCode () { return EqualityComparer<T>.Default.GetHashCode(_item); } public static bool operator ==(Wrapper<T> left, Wrapper<T> right) { return EqualityComparer<Wrapper<T>>.Default.Equals(left, right); } public static bool operator !=(Wrapper<T> left, Wrapper<T> right) { return !(left == right); } }
方案二:使用記錄類型 (Record)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public record Wrapper <T >(T Item ) ;void Main (){ var items = new List<int > { 1 , 2 , 3 }; var wrappers = items.Select(item => new Wrapper<int >(item)).ToList(); var store = new List<Wrapper<int >>(); store.AddRange(wrappers); var storeContainsAnyWrappers = wrappers .Any(wrapper => store.Contains(wrapper)); }
🌟 重點提醒
相等性問題 :Wrapper 類別需要適當實作相等性比較,否則 Contains() 等方法可能無法正常工作
記錄類型優勢 :C# 9+ 的 record 類型自動提供值相等性,非常適合作為簡單的 Wrapper
延遲執行 :注意 LINQ 的延遲執行特性對 Wrapper 建立時機的影響
功能擴展 :Wrapper 模式可以用來添加日誌、快取、驗證等橫切關注點
https://ithelp.ithome.com.tw/articles/10205321