C# 如何在不复制元素的情况下从哈希集创建ReadOnlyCollection?

C# 如何在不复制元素的情况下从哈希集创建ReadOnlyCollection?,c#,base-class-library,C#,Base Class Library,我有一个私有的HashSet,它是只读属性的支持字段,该属性应返回只读集合,以便调用方无法修改该集合。所以我试着: public class MyClass { private readonly HashSet<string> _referencedColumns; public ICollection<string> ReferencedColumns { get { return new ReadOnlyCollection<

我有一个私有的
HashSet
,它是只读属性的支持字段,该属性应返回只读集合,以便调用方无法修改该集合。所以我试着:

public class MyClass
{
    private readonly HashSet<string> _referencedColumns;

    public ICollection<string> ReferencedColumns { 
        get { return new ReadOnlyCollection<string>(_referencedColumns); }
    }
公共类MyClass
{
私有只读HashSet\u referencedColumns;
公共ICollection ReferencedColumns{
获取{return new ReadOnlyCollection(_referencedColumns);}
}

这不会编译为
ReadOnlyCollection
接受
IList
,而
HashSet
不会实现它。是否有另一个包装器可以用来保存复制项?出于我的目的,只需返回实现
ICollection
的内容即可(而不是
IList
)它由
HashSet
实现,请考虑将属性公开为类型
IReadOnlyCollection
,这将提供
HashSet
的只读视图。这是一种有效的实现方法,因为属性getter不需要底层集合的副本


这不会阻止某人将属性转换为<代码>哈什集并修改它。如果您对此感到担忧,请考虑<代码>返回引用引用列。在属性getter中,它将创建基础集的副本。

您可以使用以下修饰符包装哈希集并返回只读的
ICollection
(根据
ICollection
合同中的规定,
IsReadOnly
属性返回true,修改方法抛出
NotSupportedException
):

公共类MyReadOnlyCollection:ICollection
{
私有只读ICollection装饰集合;
公共MyReadOnlyCollection(ICollection\U集合)
{
装饰收集=装饰收集;
}
公共IEnumerator GetEnumerator()
{
返回decoratedCollection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return((IEnumerable)decoratedCollection).GetEnumerator();
}
公共作废新增(T项)
{
抛出新的NotSupportedException();
}
公共空间清除()
{
抛出新的NotSupportedException();
}
公共布尔包含(T项)
{
返回decoratedCollection.Contains(项目);
}
public void CopyTo(T[]数组,int arrayIndex)
{
CopyTo(数组,arrayIndex);
}
公共布尔删除(T项)
{
抛出新的NotSupportedException();
}
公共整数计数
{
获取{return decoratedCollection.Count;}
}
公共图书馆是只读的
{
获取{return true;}
}
}
您可以这样使用它:

public class MyClass
{
    private readonly HashSet<string> _referencedColumns;

    public ICollection<string> ReferencedColumns { 
        get { return new MyReadOnlyCollection<string>(_referencedColumns); }
    }
    //...
公共类MyClass
{
私有只读HashSet\u referencedColumns;
公共ICollection ReferencedColumns{
获取{返回新的MyReadOnlyCollection(_referencedColumns);}
}
//...

请注意,此解决方案不会获取哈希集的快照,而是保留对哈希集的引用。这意味着返回的集合将包含哈希集的实时版本,即,如果哈希集发生更改,则在更改之前获得只读集合的使用者将能够看到更改。

虽然它不是只读的,但微软发布了一个名为
System.Collections.Immutable的nuget包,其中包含一个
ImmutableHashSet
,它实现了
IImmutableSet
,扩展了
IReadOnlyCollection

快速使用示例:

公共类TrackedPropertiesBuilder:ITrackedPropertiesBuilder
{
私有ImmutableHashSet.Builder跟踪属性生成器;
公共跟踪属性生成器()
{
this.trackedPropertiesBuilder=ImmutableHashSet.CreateBuilder();
}
公共ITrackedPropertiesBuilder添加(字符串propertyName)
{
this.trackedPropertiesBuilder.Add(propertyName);
归还这个;
}
公共IImmutableSet生成()
=>this.trackedPropertiesBuilder.ToImmutable();
}

这很简单,我不知道Microsoft为什么不提供此功能,但我将基于
IReadOnlyCollection
发布我的实现,以及完整性的扩展方法

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);
}
公共类MyClass{
私有只读HashSet\u referencedColumns;
public IReadonlyHashSet ReferencedColumns=>\u ReferencedColumns.AsReadOnly();
}
///表示不允许添加项的哈希集。
///集合中项目的类型。
公共接口IReadonlyHashSet:IReadOnlyCollection{
///如果集合包含给定项,则返回true。
公共布尔包含(ti);
}
///仅允许查找的的包装器。
///集合中项目的类型。
公共类ReadonlyHashSet:IReadonlyHashSet{
/// 
public int Count=>set.Count;
私有哈希集;
///为给定哈希集创建新的包装器实例。
public ReadonlyHashSet(HashSet)=>this.set=set;
/// 
公共布尔包含(ti)=>set.Contains(i);
/// 
public IEnumerator GetEnumerator()=>set.GetEnumerator();
/// 
IEnumerator IEnumerable.GetEnumerator()=>set.GetEnumerator();
}
///类的扩展方法。
公共静态类HasSetExtensions{
///返回集合的只读包装。
公共静态只读哈希集AsReadOnly(此哈希集为)
=>新的只读数据集;
}

如果调用方不能修改返回值很重要,请看一看,也许[this question(),谢谢。那么是否没有可以节省复制开销且无法回滚的包装器?仅用于记录:强制转换到
IReadOnlyCollection
仅适用于.NET 4.6及更高版本(请参阅:).Good call。这仍将使用
HashSet
的索引行为,尽管这不再明显。调用
IReadOn
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);
}