2009-03-26 7 views
18

Ich möchte wirklich Hashsets in meinem Programm verwenden. Die Verwendung eines Wörterbuchs fühlt sich hässlich an. Ich werde wahrscheinlich VS2008 mit .Net 3.5 eines Tages verwenden, also mein Ideal wäre, dass, obwohl ich nicht kann (oder kann ich?) hashsets in VS2005 verwenden, wenn ich .NET 3.5 verwenden, will ich nicht viel ändern müssen, wenn überhaupt, um auf die Verwendung dieser Hashsets zu wechseln.Verwendung von HashSet in C# 2.0, kompatibel mit 3.5

Ich wundere mich, wenn jemand einer vorhandenen Hashset-Implementierung bewusst ist, die in diesem Sinne entwickelt wurde, oder eine Möglichkeit, das 3.5-Hashset in VS2005 zu verwenden.

Antwort

23

Sie können jetzt in einer 2.0-Anwendung verwenden - verweisen Sie einfach auf System.Core.dll und Sie sollten gut gehen.

Hinweis: Dies würde erfordern, dass Sie die .NET 3.5 framework installieren, die frei und getrennt von Visual Studio ist. Sobald Sie das installiert haben, haben Sie die neue System.Core-Baugruppe, die den Typ HashSet<T> enthält. Da die .NET Framework-Versionen 2.0 - 3.5 alle dieselbe CLR verwenden, können Sie diese Assembly ohne Probleme in Ihrer 2.0-Anwendung verwenden.

+1

Es erfordert jedoch, dass Sie auf XP oder höher sind, jedoch. Das ist das Problem, dem ich begegnet bin, und warum ich mein eigenes HashSet erstellen musste. Wir entwickeln auf XP und erhalten all die tollen VS 2008 Sachen, aber alle unsere Klienten sind auf Win2K und können das .NET 3.5 Framework nicht ausführen, daher müssen wir .NET 2.0 als Ziel haben. –

+1

Kann ich HashSet nicht einfach aus System.Core extrahieren und in meine Assembly kompilieren? –

+5

Ja, Sie könnten, aber das heißt nicht, dass Sie sollten. Die Rahmenlizenz verbietet genau dies. –

1

Sie können das Dictionary als Hashset mit einer using-Direktive verwenden. Nicht wirklich dasselbe, aber es könnte später die Dinge für dich vereinfachen.

2

Ich denke, PowerCollections Bibliothek sollte Ihren Bedürfnissen entsprechen. Es ist eine Open-Source-Bibliothek, die mehrere Auflistungsklassen enthält, die in .NET fehlten, einschließlich Set<T>, Bag<T>, MultiDictionary usw. Es läuft auf .NET 2.0. Ich benutze es seit einigen Jahren und bin sehr zufrieden damit.

8

könnten Sie verwenden Iesi.Collections (verwendet von NHibernate) oder Mono's HashSet

+0

+! für Link für Mono's HashSet, das war genau das, was ich hier von Google kommend gesucht habe. – PiotrK

24

Hier ist eine, die ich für 2.0 schrieb, die ein Dictionary < T, Objekt > intern verwendet. Es ist keine exakte Übereinstimmung der 3.5 HashSet <T>, aber es macht die Arbeit für mich.

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Runtime.Serialization; 

public class HashSet<T> : ICollection<T>, ISerializable, IDeserializationCallback 
{ 
    private readonly Dictionary<T, object> dict; 

    public HashSet() 
    { 
     dict = new Dictionary<T, object>(); 
    } 

    public HashSet(IEnumerable<T> items) : this() 
    { 
     if (items == null) 
     { 
      return; 
     } 

     foreach (T item in items) 
     { 
      Add(item); 
     } 
    } 

    public HashSet<T> NullSet { get { return new HashSet<T>(); } } 

    #region ICollection<T> Members 

    public void Add(T item) 
    { 
     if (null == item) 
     { 
      throw new ArgumentNullException("item"); 
     } 

     dict[item] = null; 
    } 

    /// <summary> 
    /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>. 
    /// </summary> 
    /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception> 
    public void Clear() 
    { 
     dict.Clear(); 
    } 

    public bool Contains(T item) 
    { 
     return dict.ContainsKey(item); 
    } 

    /// <summary> 
    /// Copies the items of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index. 
    /// </summary> 
    /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the items copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or-<paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.-or-The number of items in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type T cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception> 
    public void CopyTo(T[] array, int arrayIndex) 
    { 
     if (array == null) throw new ArgumentNullException("array"); 
     if (arrayIndex < 0 || arrayIndex >= array.Length || arrayIndex >= Count) 
     { 
      throw new ArgumentOutOfRangeException("arrayIndex"); 
     } 

     dict.Keys.CopyTo(array, arrayIndex); 
    } 

    /// <summary> 
    /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>. 
    /// </summary> 
    /// <returns> 
    /// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>. 
    /// </returns> 
    /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception> 
    public bool Remove(T item) 
    { 
     return dict.Remove(item); 
    } 

    /// <summary> 
    /// Gets the number of items contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>. 
    /// </summary> 
    /// <returns> 
    /// The number of items contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>. 
    /// </returns> 
    public int Count 
    { 
     get { return dict.Count; } 
    } 

    /// <summary> 
    /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. 
    /// </summary> 
    /// <returns> 
    /// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false. 
    /// </returns> 
    public bool IsReadOnly 
    { 
     get 
     { 
      return false; 
     } 
    } 

    #endregion 

    public HashSet<T> Union(HashSet<T> set) 
    { 
     HashSet<T> unionSet = new HashSet<T>(this); 

     if (null == set) 
     { 
      return unionSet; 
     } 

     foreach (T item in set) 
     { 
      if (unionSet.Contains(item)) 
      { 
       continue; 
      } 

      unionSet.Add(item); 
     } 

     return unionSet; 
    } 

    public HashSet<T> Subtract(HashSet<T> set) 
    { 
     HashSet<T> subtractSet = new HashSet<T>(this); 

     if (null == set) 
     { 
      return subtractSet; 
     } 

     foreach (T item in set) 
     { 
      if (!subtractSet.Contains(item)) 
      { 
       continue; 
      } 

      subtractSet.dict.Remove(item); 
     } 

     return subtractSet; 
    } 

    public bool IsSubsetOf(HashSet<T> set) 
    { 
     HashSet<T> setToCompare = set ?? NullSet; 

     foreach (T item in this) 
     { 
      if (!setToCompare.Contains(item)) 
      { 
       return false; 
      } 
     } 

     return true; 
    } 

    public HashSet<T> Intersection(HashSet<T> set) 
    { 
     HashSet<T> intersectionSet = NullSet; 

     if (null == set) 
     { 
      return intersectionSet; 
     } 

     foreach (T item in this) 
     { 
      if (!set.Contains(item)) 
      { 
       continue; 
      } 

      intersectionSet.Add(item); 
     } 

     foreach (T item in set) 
     { 
      if (!Contains(item) || intersectionSet.Contains(item)) 
      { 
       continue; 
      } 

      intersectionSet.Add(item); 
     } 

     return intersectionSet; 
    } 

    public bool IsProperSubsetOf(HashSet<T> set) 
    { 
     HashSet<T> setToCompare = set ?? NullSet; 

     // A is a proper subset of a if the b is a subset of a and a != b 
     return (IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this)); 
    } 

    public bool IsSupersetOf(HashSet<T> set) 
    { 
     HashSet<T> setToCompare = set ?? NullSet; 

     foreach (T item in setToCompare) 
     { 
      if (!Contains(item)) 
      { 
       return false; 
      } 
     } 

     return true; 
    } 

    public bool IsProperSupersetOf(HashSet<T> set) 
    { 
     HashSet<T> setToCompare = set ?? NullSet; 

     // B is a proper superset of a if b is a superset of a and a != b 
     return (IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this)); 
    } 

    public List<T> ToList() 
    { 
     return new List<T>(this); 
    } 

    #region Implementation of ISerializable 

    /// <summary> 
    /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object. 
    /// </summary> 
    /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param><param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param><exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception> 
    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     if (info == null) throw new ArgumentNullException("info"); 
     dict.GetObjectData(info, context); 
    } 

    #endregion 

    #region Implementation of IDeserializationCallback 

    /// <summary> 
    /// Runs when the entire object graph has been deserialized. 
    /// </summary> 
    /// <param name="sender">The object that initiated the callback. The functionality for this parameter is not currently implemented. </param> 
    public void OnDeserialization(object sender) 
    { 
     dict.OnDeserialization(sender); 
    } 

    #endregion 

    #region Implementation of IEnumerable 

    /// <summary> 
    /// Returns an enumerator that iterates through the collection. 
    /// </summary> 
    /// <returns> 
    /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection. 
    /// </returns> 
    /// <filterpriority>1</filterpriority> 
    public IEnumerator<T> GetEnumerator() 
    { 
     return dict.Keys.GetEnumerator(); 
    } 

    /// <summary> 
    /// Returns an enumerator that iterates through a collection. 
    /// </summary> 
    /// <returns> 
    /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection. 
    /// </returns> 
    /// <filterpriority>2</filterpriority> 
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    #endregion 
} 
+1

Ok, schlag mich runter wenn nötig, aber du hast den Punkt eines HashSets hier wirklich besiegt. Sie haben die Funktionalität der HashSet.Contains() - Methode durch die Dictionary.ConatinsKey() -Methode ersetzt. Diese Methode ist O (n), während die Methode HashSet.Contains() O (1) ist. Diese Implementierung hat die volle Geschwindigkeit eines List Objekts. – SamuelWarren

+0

@highphilosopher: Dies war nur eine schnelle und dreckige Annäherung eines HashSet .Was ich tatsächlich tun würde, und was ich jetzt tue, da ich Benutzer auf Win2K unterstützen muss, während ich HashSet s und andere 3.5 Goodies benutze, ist, Monos System.Core für 2.0 neu zu kompilieren und in VS 2008 zu entwickeln. Der obige Code diente meinen Zwecken für die paar Wochen, in denen ich ihn benutzte, und ist nicht dazu gedacht, für irgendetwas außer kleinen Datensätzen verwendet zu werden. –

+14

@highphilosopher: Das ist falsch. Für den Datensatz sind 'Dictionary <>' Schlüsselmethoden (einschließlich 'ConstainsKey') gehashte O (1) Implementierungen und sehr ähnlich zu HashSet Methoden mit einem ähnlichen Zweck. 'ContainsValue' ist O (n). Eine der Aufgaben von Dictionary besteht darin, eine schnelle Suche nach Schlüsseln bereitzustellen, anstatt einen Index als Array zu verwenden. –

Verwandte Themen