我有一个私有HashSet<string>
它是只读属性的支持字段,该属性应返回只读集合,以便调用者无法修改集合。所以我尝试:
public class MyClass
{
private readonly HashSet<string> _referencedColumns;
public ICollection<string> ReferencedColumns {
get { return new ReadOnlyCollection<string>(_referencedColumns); }
}
这不会编译,因为ReadOnlyCollection
接受不受HashSet<T>
约束的IList<T>
。是否有另一个包装器可用于保存我复制项目?就我的目的而言,只需返回由HashSet<T>
实现的实现ICollection<T>
(而不是IList<T>
(的东西就足够了。
请考虑改为将属性公开为类型 IReadOnlyCollection<>
,这将提供HashSet<>
的只读视图。这是实现此目的的有效方法,因为属性 getter 不需要基础集合的副本。
这不会阻止某人将属性强制转换为HashSet<>
并对其进行修改。如果您担心这一点,请考虑在属性 getter 中return _referencedColumns.ToList()
,这将创建基础集的副本。
虽然它不是只读的,但Microsoft发布了一个名为 System.Collections.Immutable
的 nuget 包,其中包含一个实现扩展IReadOnlyCollection<T>
IImmutableSet<T>
的ImmutableHashSet<T>
快速使用示例:
public class TrackedPropertiesBuilder : ITrackedPropertiesBuilder
{
private ImmutableHashSet<string>.Builder trackedPropertiesBuilder;
public TrackedPropertiesBuilder()
{
this.trackedPropertiesBuilder = ImmutableHashSet.CreateBuilder<string>();
}
public ITrackedPropertiesBuilder Add(string propertyName)
{
this.trackedPropertiesBuilder.Add(propertyName);
return this;
}
public IImmutableSet<string> Build()
=> this.trackedPropertiesBuilder.ToImmutable();
}
以下装饰器包装哈希集并返回只读ICollection<T>
(IsReadOnly
属性返回 true,修改方法抛出NotSupportedException
,如 ICollection<T>
的协定中指定(:
public class MyReadOnlyCollection<T> : ICollection<T>
{
private readonly ICollection<T> decoratedCollection;
public MyReadOnlyCollection(ICollection<T> decorated_collection)
{
decoratedCollection = decorated_collection;
}
public IEnumerator<T> GetEnumerator()
{
return decoratedCollection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable) decoratedCollection).GetEnumerator();
}
public void Add(T item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(T item)
{
return decoratedCollection.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
decoratedCollection.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
throw new NotSupportedException();
}
public int Count
{
get { return decoratedCollection.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
}
你可以像这样使用它:
public class MyClass
{
private readonly HashSet<string> _referencedColumns;
public ICollection<string> ReferencedColumns {
get { return new MyReadOnlyCollection<string>(_referencedColumns); }
}
//...
请注意,此解决方案不会拍摄 HashSet 的快照,而是保存对 HashSet 的引用。这意味着返回的集合将包含 HashSet 的实时版本,即,如果 HashSet 发生更改,则在更改之前获得只读集合的使用者将能够看到更改。
很简单,我不知道为什么Microsoft没有提供这个,但我将发布基于 IReadOnlyCollection<T>
的实现,以及完整性的扩展方法。
public class MyClass {
private readonly HashSet<string> _referencedColumns;
public IReadonlyHashSet<string> ReferencedColumns => _referencedColumns.AsReadOnly();
}
/// <summary>Represents hash set which don't allow for items addition.</summary>
/// <typeparam name="T">Type of items int he set.</typeparam>
public interface IReadonlyHashSet<T> : IReadOnlyCollection<T> {
/// <summary>Returns true if the set contains given item.</summary>
public bool Contains(T i);
}
/// <summary>Wrapper for a <see cref="HashSet{T}"/> which allows only for lookup.</summary>
/// <typeparam name="T">Type of items in the set.</typeparam>
public class ReadonlyHashSet<T> : IReadonlyHashSet<T> {
/// <inheritdoc/>
public int Count => set.Count;
private HashSet<T> set;
/// <summary>Creates new wrapper instance for given hash set.</summary>
public ReadonlyHashSet(HashSet<T> set) => this.set = set;
/// <inheritdoc/>
public bool Contains(T i) => set.Contains(i);
/// <inheritdoc/>
public IEnumerator<T> GetEnumerator() => set.GetEnumerator();
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => set.GetEnumerator();
}
/// <summary>Extension methods for the <see cref="HashSet{T}"/> class.</summary>
public static class HasSetExtensions {
/// <summary>Returns read-only wrapper for the set.</summary>
public static ReadonlyHashSet<T> AsReadOnly<T>(this HashSet<T> s)
=> new ReadonlyHashSet<T>(s);
}