C# HashSet<T> 只读解决方法
C# HashSet<T> read-only workaround
这是示例代码:
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 的 "AsReadOnly" 方法吗?
这里是 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>
class 接受 ISet<T>
并且只通过读取操作 like ReadOnlyCollection<T>
does internally.
更新:
这是一个完全充实的 ReadOnlySet<T>
我很快写了一个扩展方法,该方法将 .AsReadOnly()
添加到任何实现 ISet<T>
的东西上
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; }
}
}
您可以编写自己的 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); }
}
我认为这会解决问题。
HashSet implements the IReadOnlyCollection interface starting
with the .NET Framework 4.6; in previous versions of the .NET
Framework, the HashSet class did not implement this interface.
在 .NET Framework 4.6 版本中,HashSet 实现了 IReadOnlyCollection 接口和 ISet 接口。 link...
好像是。
您也可以这样做,但可能会影响性能:
var foo = (IReadOnlyCollection<string>) mySet.toList();
从 .NET 5 开始,字典的 HashSet<T>
class now implements the IReadOnlySet<T>
interface. There is no built-in ReadOnlySet<T>
wrapper though, analogous to the existing ReadOnlyDictionary<TKey, TValue>
class,但实现一个是微不足道的:
public class ReadOnlySet<T> : IReadOnlySet<T>
{
private readonly ISet<T> _set;
public ReadOnlySet(ISet<T> set) { ArgumentNullException.ThrowIfNull(set); _set = set; }
public int Count => _set.Count;
public bool Contains(T item) => _set.Contains(item);
public bool IsProperSubsetOf(IEnumerable<T> other) => _set.IsProperSubsetOf(other);
public bool IsProperSupersetOf(IEnumerable<T> other) => _set.IsProperSupersetOf(other);
public bool IsSubsetOf(IEnumerable<T> other) => _set.IsSubsetOf(other);
public bool IsSupersetOf(IEnumerable<T> other) => _set.IsSupersetOf(other);
public bool Overlaps(IEnumerable<T> other) => _set.Overlaps(other);
public bool SetEquals(IEnumerable<T> other) => _set.SetEquals(other);
public IEnumerator<T> GetEnumerator() => _set.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
即将推出的 .NET 7 也将为 IDictionary<TKey,TValue>
s 提供一个新的 AsReadOnly
扩展方法,所以让我们也为 ISet<T>
s 做一个:
public static ReadOnlySet<T> AsReadOnly<T>(this ISet<T> set) => new ReadOnlySet<T>(set);
用法示例:
HashSet<Item> items = new();
ReadOnlySet<Item> readOnlyItems = items.AsReadOnly();
这是示例代码:
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
这里是 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>
class 接受 ISet<T>
并且只通过读取操作 like ReadOnlyCollection<T>
does internally.
更新:
这是一个完全充实的 ReadOnlySet<T>
我很快写了一个扩展方法,该方法将 .AsReadOnly()
添加到任何实现 ISet<T>
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; }
}
}
您可以编写自己的 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); }
}
我认为这会解决问题。
HashSet implements the IReadOnlyCollection interface starting with the .NET Framework 4.6; in previous versions of the .NET Framework, the HashSet class did not implement this interface.
在 .NET Framework 4.6 版本中,HashSet 实现了 IReadOnlyCollection 接口和 ISet 接口。 link... 好像是。
您也可以这样做,但可能会影响性能:
var foo = (IReadOnlyCollection<string>) mySet.toList();
从 .NET 5 开始,字典的 HashSet<T>
class now implements the IReadOnlySet<T>
interface. There is no built-in ReadOnlySet<T>
wrapper though, analogous to the existing ReadOnlyDictionary<TKey, TValue>
class,但实现一个是微不足道的:
public class ReadOnlySet<T> : IReadOnlySet<T>
{
private readonly ISet<T> _set;
public ReadOnlySet(ISet<T> set) { ArgumentNullException.ThrowIfNull(set); _set = set; }
public int Count => _set.Count;
public bool Contains(T item) => _set.Contains(item);
public bool IsProperSubsetOf(IEnumerable<T> other) => _set.IsProperSubsetOf(other);
public bool IsProperSupersetOf(IEnumerable<T> other) => _set.IsProperSupersetOf(other);
public bool IsSubsetOf(IEnumerable<T> other) => _set.IsSubsetOf(other);
public bool IsSupersetOf(IEnumerable<T> other) => _set.IsSupersetOf(other);
public bool Overlaps(IEnumerable<T> other) => _set.Overlaps(other);
public bool SetEquals(IEnumerable<T> other) => _set.SetEquals(other);
public IEnumerator<T> GetEnumerator() => _set.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
即将推出的 .NET 7 也将为 IDictionary<TKey,TValue>
s 提供一个新的 AsReadOnly
扩展方法,所以让我们也为 ISet<T>
s 做一个:
public static ReadOnlySet<T> AsReadOnly<T>(this ISet<T> set) => new ReadOnlySet<T>(set);
用法示例:
HashSet<Item> items = new();
ReadOnlySet<Item> readOnlyItems = items.AsReadOnly();