C# HashSet<T> 只读解决方法
Posted
技术标签:
【中文标题】C# HashSet<T> 只读解决方法【英文标题】:C# HashSet<T> read-only workaround 【发布时间】:2016-08-17 08:07:04 【问题描述】:这是示例代码:
static class Store
private static List<String> strList = new List<string>();
private static HashSet<String> strHashSet = new HashSet<string>();
public static List<String> NormalList
get return strList;
public static HashSet<String> NormalHashSet
get return strHashSet;
public static IReadOnlyList<String> ReadonlyList
get return (IReadOnlyList<String>)strList;
public static IReadOnlyCollection<String> ReadonlyHashSet
get return (IReadOnlyCollection<String>)strHashSet;
public static IReadOnlyList<String> Real_ReadonlyList
get return (IReadOnlyList<String>)strList.AsReadOnly();
public static IReadOnlyCollection<String> Real_ReadonlyHashSet
get
List<String> tmpList = new List<String>(strHashSet);
return (IReadOnlyList<String>)(tmpList).AsReadOnly();
这是一个测试代码:
// normal behaviour
// you can modify the list and the hashset
Store.NormalList.Add("some string 1");
Store.NormalHashSet.Add("some string 1");
// tricky behaviour
// you can still modify the list and the hashset
((List<String>)Store.ReadonlyList).Add("some string 2");
((HashSet<String>)Store.ReadonlyHashSet).Add("some string 2");
// expected read-only behaviour
// you can NOT modify
// throws InvalidCastException
((List<String>)Store.Real_ReadonlyList).Add("some string 3");
// throws InvalidCastException
((HashSet<String>)Store.Real_ReadonlyHashSet).Add("some string 3");
我的问题是:
“Real_ReadonlyHashSet”属性是否有更好的解决方案?
微软有一天会为 HashSet
【问题讨论】:
有一个ImmutableHashSet 自己写也没那么难:github.com/airbreather/AirBreather.Common/blob/…github.com/airbreather/AirBreather.Common/blob/… 【参考方案1】:这里是the entirety of the code 的.AsReadOnly()
public ReadOnlyCollection<T> AsReadOnly()
Contract.Ensures(Contract.Result<ReadOnlyCollection<T>>() != null);
return new ReadOnlyCollection<T>(this);
如果您不使用 CodeContracts,则甚至不需要第一行。但是ReadOnlyCollection<T>
只支持IList<T>
,HashSet<T>
不支持。
我要做的是创建自己的 ReadOnlySet<T>
类,该类接受 ISet<T>
并且仅通过读取操作 like ReadOnlyCollection<T>
does internally。
更新:
这是一个完全充实的ReadOnlySet<T>
,我很快写了一个扩展方法,在任何实现ISet<T>
的东西上添加.AsReadOnly()
public static class SetExtensionMethods
public static ReadOnlySet<T> AsReadOnly<T>(this ISet<T> set)
return new ReadOnlySet<T>(set);
public class ReadOnlySet<T> : IReadOnlyCollection<T>, ISet<T>
private readonly ISet<T> _set;
public ReadOnlySet(ISet<T> set)
_set = set;
public IEnumerator<T> GetEnumerator()
return _set.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return ((IEnumerable) _set).GetEnumerator();
void ICollection<T>.Add(T item)
throw new NotSupportedException("Set is a read only set.");
public void UnionWith(IEnumerable<T> other)
throw new NotSupportedException("Set is a read only set.");
public void IntersectWith(IEnumerable<T> other)
throw new NotSupportedException("Set is a read only set.");
public void ExceptWith(IEnumerable<T> other)
throw new NotSupportedException("Set is a read only set.");
public void SymmetricExceptWith(IEnumerable<T> other)
throw new NotSupportedException("Set is a read only set.");
public bool IsSubsetOf(IEnumerable<T> other)
return _set.IsSubsetOf(other);
public bool IsSupersetOf(IEnumerable<T> other)
return _set.IsSupersetOf(other);
public bool IsProperSupersetOf(IEnumerable<T> other)
return _set.IsProperSupersetOf(other);
public bool IsProperSubsetOf(IEnumerable<T> other)
return _set.IsProperSubsetOf(other);
public bool Overlaps(IEnumerable<T> other)
return _set.Overlaps(other);
public bool SetEquals(IEnumerable<T> other)
return _set.SetEquals(other);
public bool Add(T item)
throw new NotSupportedException("Set is a read only set.");
public void Clear()
throw new NotSupportedException("Set is a read only set.");
public bool Contains(T item)
return _set.Contains(item);
public void CopyTo(T[] array, int arrayIndex)
_set.CopyTo(array, arrayIndex);
public bool Remove(T item)
throw new NotSupportedException("Set is a read only set.");
public int Count
get return _set.Count;
public bool IsReadOnly
get return true;
【讨论】:
能否请您为 ImmutableHashSet 添加更新?问题下方的评论很容易被忽略。 @webbertee 不可变对象行与只读对象行不同。不可变用于并发......我不是这方面的专家,但是使用一个代替另一个可能导致各种问题,即使它们很微妙。【参考方案2】:您可以编写自己的 IReadOnlyCollection<T>
实现,其中包含 IEnumerable<T>
和计数:
public sealed class ReadOnlyCollectionFromEnumerable<T>: IReadOnlyCollection<T>
readonly IEnumerable<T> _data;
public ReadOnlyCollectionFromEnumerable(IEnumerable<T> data, int count)
_data = data;
Count = count;
public IEnumerator<T> GetEnumerator()
return _data.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
public int Count get;
然后你像这样声明你的ReadonlyHashSet
属性:
public static IReadOnlyCollection<String> ReadonlyHashSet
get return new ReadOnlyCollectionFromEnumerable<string>(strHashSet, strHashSet.Count);
我认为这会解决问题。
【讨论】:
我认为传递ICollection<T>
会更好,这样你就可以传递.Contains(
,这是HashSet 中最强大的部分。
@ScottChamberlain 我保持返回类型与 OP 中的相同 - 当然,Contains()
不是IReadOnlyCollection<T>
的成员。我想 OP 想通过类型而不是属性(IsReadOnly
)表明该值是只读的
啊,我要去的是ReadOnlyCollection<T>
,哪个does pass it along,而不是IReadOnlyCollection<T>
@ScottChamberlain 虽然很奇怪,不是吗?微软在这方面似乎不太一致……【参考方案3】:
HashSet 实现 IReadOnlyCollection 接口启动 使用 .NET Framework 4.6;在 .NET 的早期版本中 框架,HashSet类没有实现这个接口。
Read in docs.microsoft.com
【讨论】:
但是,IReadOnlyCollection<T>
没有公开 Boolean Contains(T item)
方法。
@Dai 你是对的,但是当前实现 Enumerable.Contains() 扩展方法试图将类型转换为 ICollection 并且如果成功调用包含在 ICollection 上(转换为 ICollection 成功,因为 HashSet 实现了 ICollection) - 和该方法的实现将包含在 HashSet 中。 referencesource.microsoft.com/#System.Core/System/Linq/…【参考方案4】:
在 .NET Framework 4.6 版本中,HashSet 实现了 IReadOnlyCollection 接口以及 ISet 接口。 link... 似乎确实如此。
您也可以这样做,但可能会影响性能:
var foo = (IReadOnlyCollection<string>) mySet.toList();
【讨论】:
以上是关于C# HashSet<T> 只读解决方法的主要内容,如果未能解决你的问题,请参考以下文章
不变性/只读语义(特别是 C# IReadOnlyCollection<T>)
C#常见容器ArrayListListHashSetHashtable DictionaryStackQueue