Added HashSet<T> for compatibility with .NET 2.0

pull/21/head
David Hall 2018-08-28 14:51:01 -06:00
parent 51df9976fa
commit 9c0c3b2967
4 changed files with 532 additions and 2 deletions

View File

@ -2,6 +2,9 @@
### Classes
Class | Description
---- | ----
BitHelper | Static methods to help with bit manipulation.
ByteSizeFormatter | A custom formatter for byte sizes (things like files, network bandwidth, etc.) that will automatically determine the best abbreviation.
ComConnectionPoint | Helper class to create an advised COM sink. When this class is constructed, the source is queried for an `IConnectionPointContainer` reference.
ComReleaser<T> | A safe variable to hold an instance of a COM class that automatically calls `Marshal.ReleaseComObject(System.Object)` on disposal.
CorrespondingTypeAttribute | Attribute for enum values that provides information about corresponding types and related actions. Useful for Get/Set methods that use an enumeration value to determine the type to get or set.
CoTaskMemoryMethods | Unmanaged memory methods for COM.
@ -9,6 +12,8 @@ EnumerableEqualityComparer<T> | Checks the linear equality of two enumerated lis
EnumExtensions | Extensions for enumerated types.
EventedList<T> | A generic list that provides event for changes to the list. This is an alternative to ObservableCollection that provides distinct events for each action (add, insert, remove, changed).
FileTimeExtensions | Extensions for `FILETIME`.
Formatter | Base class for expandable formatters.
FormatterComposer | Extension method to combine formatter instances.
GenericSafeHandle | A `SafeHandle` that takes a delegate in the constructor that closes the supplied handle.
HGlobalMemoryMethods | Unmanaged memory methods for HGlobal.
InteropExtensions | Extension methods for System.Runtime.InteropServices.

View File

@ -0,0 +1,521 @@
#if NET20
using System.Linq;
using System.Runtime.Serialization;
namespace System.Collections.Generic
{
/// <summary>Provides the base interface for the abstraction of sets.</summary>
public interface ISet<T> : ICollection<T>
{
/// <summary>Adds an element to the current set and returns a value to indicate if the element was successfully added.</summary>
/// <param name="item">The element to add to the set.</param>
/// <returns><c>true</c> if the element is added to the set; <c>false</c> if the element is already in the set.</returns>
new bool Add(T item);
/// <summary>Removes all elements in the specified collection from the current set.</summary>
/// <param name="other">The collection of items to remove from the set.</param>
void ExceptWith(IEnumerable<T> other);
/// <summary>Modifies the current set so that it contains only elements that are also in a specified collection.</summary>
/// <param name="other">The collection to compare to the current set.</param>
void IntersectWith(IEnumerable<T> other);
/// <summary>Determines whether the current set is a proper (strict) subset of a specified collection.</summary>
/// <param name="other">The collection to compare to the current set.</param>
/// <returns><c>true</c> if the current set is a proper subset of <paramref name="other"/>; otherwise, <c>false</c>.</returns>
bool IsProperSubsetOf(IEnumerable<T> other);
/// <summary>Determines whether the current set is a proper (strict) superset of a specified collection.</summary>
/// <param name="other">The collection to compare to the current set.</param>
/// <returns><c>true</c> if the current set is a proper superset of <paramref name="other"/>; otherwise, <c>false</c>.</returns>
bool IsProperSupersetOf(IEnumerable<T> other);
/// <summary>Determines whether a set is a subset of a specified collection.</summary>
/// <param name="other">The collection to compare to the current set.</param>
/// <returns><c>true</c> if the current set is a subset of <paramref name="other"/>; otherwise, <c>false</c>.</returns>
bool IsSubsetOf(IEnumerable<T> other);
/// <summary>Determines whether the current set is a superset of a specified collection.</summary>
/// <param name="other">The collection to compare to the current set.</param>
/// <returns><c>true</c> if the current set is a superset of <paramref name="other"/>; otherwise, <c>false</c>.</returns>
bool IsSupersetOf(IEnumerable<T> other);
/// <summary>Determines whether the current set overlaps with the specified collection.</summary>
/// <param name="other">The collection to compare to the current set.</param>
/// <returns><c>true</c> if the current set and <paramref name="other"/> share at least one common element; otherwise, <c>false</c>.</returns>
bool Overlaps(IEnumerable<T> other);
/// <summary>Determines whether the current set and the specified collection contain the same elements.</summary>
/// <param name="other">The collection to compare to the current set.</param>
/// <returns><c>true</c> if the current set is equal to <paramref name="other"/>; otherwise, <c>false</c>.</returns>
bool SetEquals(IEnumerable<T> other);
/// <summary>
/// Modifies the current set so that it contains only elements that are present either in the current set or in the specified
/// collection, but not both.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param>
void SymmetricExceptWith(IEnumerable<T> other);
/// <summary>
/// Modifies the current set so that it contains all elements that are present in the current set, in the specified collection, or in both.
/// </summary>
/// <param name="other">The collection to compare to the current set.</param>
void UnionWith(IEnumerable<T> other);
}
/// <summary>
/// Represents a set of values. This is less efficient than the native implementation in .NET versions after 2.0, but functionally equivalent.
/// </summary>
/// <typeparam name="T">The type of elements in the hash set.</typeparam>
public class HashSet<T> : ISet<T>, IReadOnlyCollection<T>, ISerializable, IDeserializationCallback
{
private readonly Dictionary<T, object> dict;
/// <summary>
/// Initializes a new instance of the <see cref="HashSet{T}"/> class that is empty and uses the default equality comparer for the set type.
/// </summary>
public HashSet() : this((IEqualityComparer<T>)null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HashSet{T}"/> class that uses the default equality comparer for the set type,
/// contains elements copied from the specified collection, and has sufficient capacity to accommodate the number of elements copied..
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new set.</param>
public HashSet(IEnumerable<T> collection) : this(collection, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HashSet{T}"/> class that is empty and uses the specified equality comparer for the
/// set type..
/// </summary>
/// <param name="comparer">
/// The <see cref="IEqualityComparer{T}"/> implementation to use when comparing values in the set, or <c>null</c> to use the default
/// <see cref="EqualityComparer{T}"/> implementation for the set type.
/// </param>
public HashSet(IEqualityComparer<T> comparer)
{
dict = new Dictionary<T, object>(comparer);
}
/// <summary>
/// Initializes a new instance of the <see cref="HashSet{T}"/> class that uses the specified equality comparer for the set type,
/// contains elements copied from the specified collection, and has sufficient capacity to accommodate the number of elements copied..
/// </summary>
/// <param name="collection">The collection whose elements are copied to the new set.</param>
/// <param name="comparer">
/// The <see cref="IEqualityComparer{T}"/> implementation to use when comparing values in the set, or <c>null</c> to use the default
/// <see cref="EqualityComparer{T}"/> implementation for the set type.
/// </param>
public HashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer)
{
dict = new Dictionary<T, object>(comparer);
foreach (T elem in collection)
{
Add(elem);
}
}
/* ***** Unnecessary since implemented in .NET 4.7.2
/// <summary>
/// Initializes a new instance of the <see cref="HashSet{T}"/> class that is empty, but has reserved space for capacity items and
/// uses the default equality comparer for the set type..
/// </summary>
/// <param name="capacity">The initial size of the <see cref="HashSet{T}"/></param>
public HashSet(int capacity) : this(capacity, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HashSet{T}"/> class that uses the specified equality comparer for the set type, and
/// has sufficient capacity to accommodate capacity elements..
/// </summary>
/// <param name="capacity">The initial size of the <see cref="HashSet{T}"/></param>
/// <param name="comparer">
/// The <see cref="IEqualityComparer{T}"/> implementation to use when comparing values in the set, or <c>null</c> to use the default
/// <see cref="EqualityComparer{T}"/> implementation for the set type.
/// </param>
public HashSet(int capacity, IEqualityComparer<T> comparer)
{
dict = new Dictionary<T, object>(capacity, comparer);
}
*/
/// <summary>Gets the <see cref="IEqualityComparer{T}"/> object that is used to determine equality for the values in the set.</summary>
public IEqualityComparer<T> Comparer => dict.Comparer;
/// <inheritdoc cref="ICollection{T}.Count"/>
public int Count => dict.Count;
/// <inheritdoc/>
bool ICollection<T>.IsReadOnly { get; } = false;
/// <inheritdoc/>
public bool Add(T item)
{
if (null == item)
{
throw new ArgumentNullException(nameof(item));
}
if (Contains(item))
{
return false;
}
dict[item] = null;
return true;
}
/// <inheritdoc/>
public void Clear()
{
dict.Clear();
}
/// <inheritdoc/>
public bool Contains(T item)
{
return item != null && dict.ContainsKey(item);
}
/// <inheritdoc/>
public void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0 || arrayIndex >= array.Length || arrayIndex >= Count)
{
throw new ArgumentOutOfRangeException(nameof(arrayIndex));
}
dict.Keys.CopyTo(array, arrayIndex);
}
/// <inheritdoc/>
public void ExceptWith(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (Count == 0)
{
return;
}
if (other == this)
{
Clear();
return;
}
foreach (T elem in other)
{
dict.Remove(elem);
}
}
/// <inheritdoc/>
public IEnumerator<T> GetEnumerator()
{
return dict.Keys.GetEnumerator();
}
/// <inheritdoc/>
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
dict.GetObjectData(info, context);
}
/// <inheritdoc/>
public void IntersectWith(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (other is ICollection<T> c && c.Count == 0)
{
Clear();
return;
}
var l = this.ToList();
foreach (var elem in l)
{
if (!other.Contains(elem))
{
Remove(elem);
}
}
}
/// <inheritdoc/>
public bool IsProperSubsetOf(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (Count == 0 && other is ICollection<T> c)
{
return c.Count > 0;
}
CheckUniqueAndUnfoundElements(other, out int unique, out int unfound, false);
return unique == Count && unfound > 0;
}
/// <inheritdoc/>
public bool IsProperSupersetOf(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (Count == 0)
{
return false;
}
if (other is ICollection<T> c && c.Count == 0)
{
return true;
}
CheckUniqueAndUnfoundElements(other, out int unique, out int unfound, true);
return unique < Count && unfound == 0;
}
/// <inheritdoc/>
public bool IsSubsetOf(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (Count == 0)
{
return true;
}
CheckUniqueAndUnfoundElements(other, out int unique, out int unfound, false);
return unique == Count && unfound >= 0;
}
/// <inheritdoc/>
public bool IsSupersetOf(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (other is ICollection<T> c && c.Count == 0)
{
return true;
}
foreach (T elem in other)
{
if (!Contains(elem))
{
return false;
}
}
return true;
}
/// <inheritdoc/>
public virtual void OnDeserialization(object sender)
{
dict.OnDeserialization(sender);
}
/// <inheritdoc/>
public bool Overlaps(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (Count == 0)
{
return false;
}
foreach (T elem in other)
{
if (Contains(elem))
{
return true;
}
}
return false;
}
/// <inheritdoc/>
public bool Remove(T item)
{
return item != null && dict.Remove(item);
}
/// <inheritdoc/>
public bool SetEquals(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (other is ICollection<T> c && c.Count != Count)
{
return false;
}
CheckUniqueAndUnfoundElements(other, out int unique, out int unfound, true);
return unique == Count && unfound == 0;
}
/// <inheritdoc/>
public void SymmetricExceptWith(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (Count == 0)
{
UnionWith(other);
return;
}
if (other == this)
{
Clear();
return;
}
HashSet<T> oh = new HashSet<T>(other, Comparer);
List<T> dup = this.ToList();
ExceptWith(oh);
oh.ExceptWith(dup);
UnionWith(oh);
}
/// <summary>Searches the set for a given value and returns the equal value it finds, if any.</summary>
/// <param name="equalValue">The value to search for.</param>
/// <param name="actualValue">
/// The value from the set that the search found, or the default value of T when the search yielded no match.
/// </param>
/// <returns>A value indicating whether the search was successful.</returns>
/// <remarks>
/// This can be useful when you want to reuse a previously stored reference instead of a newly constructed one (so that more sharing
/// of references can occur) or to look up a value that has more complete data than the value you currently have, although their
/// comparer functions indicate they are equal.
/// </remarks>
public bool TryGetValue(T equalValue, out T actualValue)
{
foreach (T k in dict.Keys)
{
if (!Comparer.Equals(k, equalValue))
{
continue;
}
actualValue = k;
return true;
}
actualValue = default(T);
return false;
}
/// <inheritdoc/>
public void UnionWith(IEnumerable<T> other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
if (this == other)
{
return;
}
foreach (T elem in other)
{
Add(elem);
}
}
/// <inheritdoc/>
void ICollection<T>.Add(T item)
{
Add(item);
}
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void CheckUniqueAndUnfoundElements(IEnumerable<T> other, out int unique, out int unfound, bool returnIfUnfound)
{
if (Count == 0)
{
unique = 0;
unfound = other.Any() ? 1 : 0;
return;
}
unfound = 0;
unique = 0;
List<T> l = this.ToList();
BitArray bits = new BitArray(l.Count);
foreach (T o in other)
{
int index = l.IndexOf(o);
if (index >= 0)
{
if (bits[index])
{
continue;
}
// item hasn't been seen yet
bits[index] = true;
unique++;
}
else
{
unfound++;
if (returnIfUnfound)
{
break;
}
}
}
}
}
}
#endif

View File

@ -1,4 +1,4 @@
#if (NET20 || NET35 || NET40)
#if NET20 || NET35 || NET40
namespace System.Collections.Generic
{
/// <summary>Represents a strongly-typed, read-only collection of elements.</summary>
@ -22,6 +22,7 @@ namespace System.Collections.Generic
/// <see langword="true"/> if the read-only dictionary contains an element that has the specified key; otherwise, <see langword="false"/>.
/// </returns>
bool ContainsKey(TKey key);
/// <summary>Gets the value that is associated with the specified key.</summary>
/// <param name="key">The key to locate.</param>
/// <param name="value">
@ -33,13 +34,16 @@ namespace System.Collections.Generic
/// element that has the specified key; otherwise, <see langword="false"/>.
/// </returns>
bool TryGetValue(TKey key, out TValue value);
/// <summary>Gets the element that has the specified key in the read-only dictionary.</summary>
/// <value>The key to locate.</value>
/// <returns>The element that has the specified key in the read-only dictionary.</returns>
TValue this[TKey key] { get; }
/// <summary>Gets an enumerable collection that contains the keys in the read-only dictionary.</summary>
/// <value>An enumerable collection that contains the keys in the read-only dictionary.</value>
IEnumerable<TKey> Keys { get; }
/// <summary>Gets an enumerable collection that contains the values in the read-only dictionary.</summary>
/// <value>An enumerable collection that contains the values in the read-only dictionary.</value>
IEnumerable<TValue> Values { get; }

View File

@ -30,7 +30,7 @@
<PackageReleaseNotes>Currently implements:
Classes
ComReleaser&lt;T&gt;, CorrespondingTypeAttribute, CoTaskMemoryMethods, EnumerableEqualityComparer&lt;T&gt;, EnumExtensions, EventedList&lt;T&gt;, FileTimeExtensions, GenericSafeHandle, HGlobalMemoryMethods, InteropExtensions, IOExtensions, ListChangedEventArgs&lt;T&gt;, MarshalingStream, PinnedObject, ReflectionExtensions, SafeAllocatedMemoryHandle, SafeByteArray, SafeCoTaskMemHandle, SafeCoTaskMemString, SafeHGlobalHandle, SafeMemoryHandle&lt;T&gt;, SafeMemoryHandleExt&lt;T&gt;, SparseArray&lt;T&gt;, StringHelper
BitHelper, ByteSizeFormatter, ComConnectionPoint, ComReleaser&lt;T&gt;, CorrespondingTypeAttribute, CoTaskMemoryMethods, EnumerableEqualityComparer&lt;T&gt;, EnumExtensions, EventedList&lt;T&gt;, FileTimeExtensions, Formatter, FormatterComposer, GenericSafeHandle, HGlobalMemoryMethods, InteropExtensions, IOExtensions, ListChangedEventArgs&lt;T&gt;, MarshalingStream, PinnedObject, ReflectionExtensions, SafeAllocatedMemoryHandle, SafeByteArray, SafeCoTaskMemHandle, SafeCoTaskMemString, SafeHGlobalHandle, SafeMemoryHandle&lt;T&gt;, SafeMemoryHandleExt&lt;T&gt;, SparseArray&lt;T&gt;, StringHelper
Structures
EnumFlagIndexer&lt;T&gt;, StrPtrAnsi, StrPtrAuto, StrPtrUni