如何在不复制元素的情况下从 HashSet 创建 ReadOnlyCollection?
How to make a ReadOnlyCollection from a HashSet without copying the elements?
我有一个私有 HashSet<string>
,它是只读 属性 的支持字段,它应该 return 一个只读集合,以便调用者无法修改该集合。所以我尝试:
public class MyClass
{
private readonly HashSet<string> _referencedColumns;
public ICollection<string> ReferencedColumns {
get { return new ReadOnlyCollection<string>(_referencedColumns); }
}
这不会编译,因为 ReadOnlyCollection
接受一个 IList<T>
,它不是由 HashSet<T>
实现的。我可以使用另一个包装器来避免复制项目吗?就我的目的而言,仅 return 实现 ICollection<T>
(而不是 IList<T>
)的东西就足够了,它由 HashSet<T>
.
实现
考虑将 属性 公开为 IReadOnlyCollection<>
类型,这将提供 HashSet<>
的只读视图。这是一种有效的实现方式,因为 属性 getter 不需要基础集合的副本。
这不会阻止某人将 属性 转换为 HashSet<>
并对其进行修改。如果您担心这一点,请考虑 属性 getter 中的 return _referencedColumns.ToList()
,这将创建您的基础集的副本。
您可以使用以下装饰器来包装哈希集和 return 一个只读的 ICollection<T>
(IsReadOnly
属性 return s 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 的引用。这意味着 returned 集合将包含 HashSet 的实时版本,即,如果 HashSet 发生更改,则在更改之前获得只读集合的消费者将能够看到更改。
虽然 它不是只读的,但微软发布了一个名为 System.Collections.Immutable
的 nuget 包,其中包含一个 ImmutableHashSet<T>
,它实现了 IImmutableSet<T>
,扩展了IReadOnlyCollection<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();
}
这很简单,我不知道为什么微软没有提供这个,但我会 post 我的实现基于 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);
}
我有一个私有 HashSet<string>
,它是只读 属性 的支持字段,它应该 return 一个只读集合,以便调用者无法修改该集合。所以我尝试:
public class MyClass
{
private readonly HashSet<string> _referencedColumns;
public ICollection<string> ReferencedColumns {
get { return new ReadOnlyCollection<string>(_referencedColumns); }
}
这不会编译,因为 ReadOnlyCollection
接受一个 IList<T>
,它不是由 HashSet<T>
实现的。我可以使用另一个包装器来避免复制项目吗?就我的目的而言,仅 return 实现 ICollection<T>
(而不是 IList<T>
)的东西就足够了,它由 HashSet<T>
.
考虑将 属性 公开为 IReadOnlyCollection<>
类型,这将提供 HashSet<>
的只读视图。这是一种有效的实现方式,因为 属性 getter 不需要基础集合的副本。
这不会阻止某人将 属性 转换为 HashSet<>
并对其进行修改。如果您担心这一点,请考虑 属性 getter 中的 return _referencedColumns.ToList()
,这将创建您的基础集的副本。
您可以使用以下装饰器来包装哈希集和 return 一个只读的 ICollection<T>
(IsReadOnly
属性 return s 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 的引用。这意味着 returned 集合将包含 HashSet 的实时版本,即,如果 HashSet 发生更改,则在更改之前获得只读集合的消费者将能够看到更改。
虽然 它不是只读的,但微软发布了一个名为 System.Collections.Immutable
的 nuget 包,其中包含一个 ImmutableHashSet<T>
,它实现了 IImmutableSet<T>
,扩展了IReadOnlyCollection<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();
}
这很简单,我不知道为什么微软没有提供这个,但我会 post 我的实现基于 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);
}