如何在不复制元素的情况下从 HashSet 创建只读集合



我有一个私有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);
}

最新更新