介面定義一組契約,但某些實作類別不需(或不應)公開所有成員。例如 ICollection<T> 包含 Add、Remove、Clear,若要實作「唯讀集合」,這些修改方法會破壞語意。
C# 允許顯式實作介面成員:void ICollection<T>.Add(T item) { ... }。特性:
((ICollection<T>)obj).Add(item)。NotSupportedException 達成「語意上隱藏 + 執行期防護」雙重保障。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 Framework 2.0+ 已提供標準實作,位於 System.Collections.ObjectModel,原理相同:包裝 IList<T>,顯式實作 ICollection<T> 修改方法並擲回異常。建議生產代碼直接使用內建類別。
.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("字典為唯讀。");
}
IReadOnlyCollection<T>、ICollection<T> 等細粒度介面(.NET 4.5+ 已內建)。NotSupportedException,防止惡意轉型呼叫。ReadOnlyCollection<T>、IReadOnlyDictionary<TKey, TValue>(.NET 4.5+)、ImmutableArray/ImmutableDictionary(System.Collections.Immutable)等現代替代方案更完善。