首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 介面實作進階:顯式實作隱藏成員與 ReadOnlyDictionary 設計模式

     編輯:關於C#

問題情境:介面契約過寬

介面定義一組契約,但某些實作類別不需(或不應)公開所有成員。例如 ICollection<T> 包含 Add、Remove、Clear,若要實作「唯讀集合」,這些修改方法會破壞語意。

解決方案:顯式介面實作

C# 允許顯式實作介面成員:void ICollection<T>.Add(T item) { ... }。特性:

  • 成員不出現在類別公開介面,無法透過類別實例直接呼叫。
  • 僅當實例轉型為介面型別時可見:((ICollection<T>)obj).Add(item)。
  • 配合擲回 NotSupportedException 達成「語意上隱藏 + 執行期防護」雙重保障。

範例:自訂 ReadOnlyCollection<T>

public class ReadOnlyCollection<T> : ICollection<T> {
    private readonly ICollection<T> _inner;

    public ReadOnlyCollection(ICollection<T> inner) {
        _inner = inner ?? throw new ArgumentNullException(nameof(inner));
    }

    // 唯讀安全成員:直接委派
    public int Count => _inner.Count;
    public bool IsReadOnly => true;
    public bool Contains(T item) => _inner.Contains(item);
    public void CopyTo(T[] array, int index) => _inner.CopyTo(array, index);
    public IEnumerator<T> GetEnumerator() => _inner.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // 修改成員:顯式實作 + 擲回異常
    void ICollection<T>.Add(T item) => throw new NotSupportedException("集合為唯讀。");
    void ICollection<T>.Clear() => throw new NotSupportedException("集合為唯讀。");
    bool ICollection<T>.Remove(T item) => throw new NotSupportedException("集合為唯讀。");
}

.NET 內建實作:System.Collections.ObjectModel.ReadOnlyCollection<T>

.NET Framework 2.0+ 已提供標準實作,位於 System.Collections.ObjectModel,原理相同:包裝 IList<T>,顯式實作 ICollection<T> 修改方法並擲回異常。建議生產代碼直接使用內建類別。

延伸:實作 ReadOnlyDictionary<TKey, TValue>

.NET 早期版本缺少唯讀字典,可仿照上述模式自行實作:

public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
    private readonly IDictionary<TKey, TValue> _inner;

    public ReadOnlyDictionary(IDictionary<TKey, TValue> inner) {
        _inner = inner ?? throw new ArgumentNullException(nameof(inner));
    }

    // 唯讀成員委派
    public TValue this[TKey key] => _inner[key];
    public ICollection<TKey> Keys => _inner.Keys;
    public ICollection<TValue> Values => _inner.Values;
    public int Count => _inner.Count;
    public bool IsReadOnly => true;
    public bool ContainsKey(TKey key) => _inner.ContainsKey(key);
    public bool TryGetValue(TKey key, out TValue value) => _inner.TryGetValue(key, out value);
    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _inner.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // 修改成員:顯式實作 + 異常
    void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) => Throw();
    void ICollection<KeyValuePair<TKey, TValue>>.Clear() => Throw();
    bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) => Throw();
    void IDictionary<TKey, TValue>.Add(TKey key, TValue value) => Throw();
    bool IDictionary<TKey, TValue>.Remove(TKey key) => Throw();

    private void Throw() => throw new NotSupportedException("字典為唯讀。");
}

設計原則總結

  1. 介面隔離原則 (ISP):若介面過大,考慮拆分為 IReadOnlyCollection<T>、ICollection<T> 等細粒度介面(.NET 4.5+ 已內建)。
  2. 顯式實作:僅用於「必須實作介面但不想暴露於類別公共 API」的情境。
  3. 防禦性程式設計:即使隱藏,仍應在顯式實作中擲回 NotSupportedException,防止惡意轉型呼叫。
  4. 優先使用標準庫:ReadOnlyCollection<T>、IReadOnlyDictionary<TKey, TValue>(.NET 4.5+)、ImmutableArray/ImmutableDictionary(System.Collections.Immutable)等現代替代方案更完善。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved