2

I'm creating my own HashSet that works as the standard HashSet, using a Dictionary. I'm doing this because C# for XNA XBox doesn't support HashSets.

This code is based on code from an example I found. I've edited the example to fix some of the problems but it still won't compile.

public class HashSet2<T> : ICollection<T>
{
    private Dictionary<T, Int16> dict;

    // code has been edited out of this example
    // see further on in the question for the full class

    public IEnumerator<T> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return dict.GetEnumerator();
    }
}

.

'HashSet2<T>' does not implement interface member
'System.Collections.IEnumerable.GetEnumerator()'.
'HashSet2<T>.GetEnumerator()' cannot implement
'System.Collections.IEnumerable.GetEnumerator()'
because it does not have the matching return type of
'System.Collections.IEnumerator'

I'd also be grateful for information on fixing it to be more like the standard HashSet if it deviates in it's behaviour or what it implments in ways that could be unexpected.

Continued from: stackoverflow.com/questions/9966336/c-sharp-xna-xbox-hashset-and-tuple

The most recent vertion of the class:

public class HashSet2<T> : ICollection<T>
{
    private Dictionary<T, Int16> dict;
    // Dictionary<T, bool>

    public HashSet2()
    {
        dict = new Dictionary<T, short>();
    }

    public HashSet2(HashSet2<T> from)
    {
        dict = new Dictionary<T, short>();
        foreach (T n in from)
            dict.Add(n, 0);
    }

    public void Add(T item)
    {
        // The key of the dictionary is used but not the value.
        dict.Add(item, 0);
    }

    public void Clear()
    {
        dict.Clear();
    }

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

    public void CopyTo(
        T[] array,
        int arrayIndex)
    {
        throw new NotImplementedException();
    }

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

    public System.Collections.IEnumerator GetEnumerator()
    {
        return ((System.Collections.IEnumerable)
            dict.Keys).GetEnumerator();
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return ((IEnumerable<T>)
            dict.Keys).GetEnumerator();
    }

    public int Count
    {
        get {return dict.Keys.Count;}
    }

    public bool IsReadOnly
    {
        get {return false;}
    }
}
Community
  • 1
  • 1
alan2here
  • 3,223
  • 6
  • 37
  • 62
  • 2
    You could pull the reference source code for .NET and use it to aid you in your project. – Michael J. Gray Apr 20 '12 at 12:53
  • I've been told that I can't do this here at SO. Apparently it's to do with being closed source. – alan2here Apr 20 '12 at 12:54
  • 1
    @alan2here: you can see the .NET source just fine. Go get `ILSpy` or `Reflector` or some other disassembler, open System.Core.dll and browse to System.Collections.Generic.HashSet – goric Apr 20 '12 at 12:58
  • @MichaelJ.Gray Doesn't sound legal to me. But he could use Mono's hashset, which is published under MIT X11: https://github.com/mono/mono/blob/master/mcs/class/System.Core/System.Collections.Generic/HashSet.cs – CodesInChaos Apr 20 '12 at 13:13
  • @CodeInChaos Microsoft effectively tells the developer how to do this by explaining [How to: Step Into a System Call](http://msdn.microsoft.com/en-us/library/cc667409.aspx). – Joshua Drake Apr 20 '12 at 13:17
  • There are hundreds of errors! – alan2here Apr 20 '12 at 13:18
  • @JoshuaDrake And where in that article does microsoft say that you may copy and use that code? AFAIK the license for the reference source is *very* restrictive. – CodesInChaos Apr 20 '12 at 13:18
  • 3
    I hate to break it to all of you, but Microsoft has released the source code for the .NET framework a LONG time ago... It can be found at http://referencesource.microsoft.com/netframework.aspx and I use it all the time when I'm curious about how something works in .NET world. – SPFiredrake Apr 20 '12 at 14:44

4 Answers4

1

The point is that the HashSet's GetEnumerator returns enumerator that enumerates keys of type T while dictionary's GetEnumerator returns enumerator that enumerates KeyValue object.

UPDATE

Change it to below:

public IEnumerator GetEnumerator()
{
    dict.Keys.GetEnumerator();
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
    return dict.Keys.GetEnumerator();
}
Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • IEnumerator in "public IEnumerator GetEnumerator()" gives the error "Using the generic type 'System.Collections.Generic.IEnumerator' requires 1 type argument." – alan2here Apr 20 '12 at 13:31
1

You want to enumerate the keys, not the dictionary. Try this:

public IEnumerator GetEnumerator()
{
    return ((IEnumerable)dict.Keys).GetEnumerator();
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
    return ((IEnumerable<T>)dict.Keys).GetEnumerator();
}
Kendall Frey
  • 43,130
  • 20
  • 110
  • 148
  • I get "'HashSet2' does not implement interface member 'System.Collections.IEnumerable.GetEnumerator()'. 'HashSet2.GetEnumerator()' cannot implement 'System.Collections.IEnumerable.GetEnumerator()' because it does not have the matching return type of 'System.Collections.IEnumerator'." – alan2here Apr 20 '12 at 14:19
  • IEnumerator and IEnumerable in "public IEnumerator GetEnumerator()" and "return ((IEnumerable)dict.Keys).GetEnumerator();" respectivly give "Using the generic type 'System.Collections.Generic.IEnumerator' requires 1 type arguments". The class also still gives an error. – alan2here Apr 20 '12 at 16:30
  • Try `using System.Collections;` – Kendall Frey Apr 20 '12 at 16:31
0

You can simply use Mono's HashSet<T>. You might need to make some minor changes to #if, or remove some interfaces/attributes, but it works on .net.

It's using the MIT X11 license, which is permissive. https://github.com/mono/mono/blob/master/mcs/class/System.Core/System.Collections.Generic/HashSet.cs

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • It's a possiblity, although I can't spend loads of time trying to get there library to install and intergrate with my program, editing it to fix it because it's out of date or for the wrong enviroment or uses #if or something which itself breaks more stuff, a day or two in when it's all installed and stuff and there are several more questions here about how to do it, then I eventually give up and still dont have a solution. Thanks for the answer, and I'll look into it, but after I've looked at other possibilities. – alan2here Apr 20 '12 at 13:28
  • In made the file compile in normal .net 4 in two minutes. It doesn't need any other parts of mono. I had to remove the `DebuggerTypeProxy` attribute, but that's a minor change. – CodesInChaos Apr 20 '12 at 13:30
  • Cool, thanks. Theres also the how similar to the standard HashSet and if not is it code I can understand so I can change it stuff. Still feels like a potentially recursive can of worms. I think there might be a simpler solution in the answer by Aliostad but it's not quite there. If not I'll reply here later. – alan2here Apr 20 '12 at 13:47
0

Just took a look at the source, and all implementations of GetEnumerator in the Dictionary<TKey, TValue> return the KeyCollection.Enumerator/ValueCollection.Enumerator objects instead of IEnumerator<T> (which is what we need). The good news is that the Key/ValueCollation.Enumerator implement both System.Collection.IEnumerator and IEnumerator<T> interfaces, so you can safely cast to those types.

Try doing this instead:

public IEnumerator GetEnumerator()
{
    return (IEnumerator)dict.Keys.GetEnumerator();
}

IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
    return (IEnumerator<T>)dict.Keys.GetEnumerator();
}
SPFiredrake
  • 3,852
  • 18
  • 26