1

I have the following class and a HashSet of it:

public class VersionSettings {
  public String Culture { get; set; }
  public String Domain { get; set; }
  public String Name { get; set; }    
}

HashSet<VersionSetting> vs = ...

Questions

  1. I would like two VersionSettings to be equal when Culture is the same.

    In this moment it is not what happens. How can i solve this?

  2. Would be possible to get a VersionSetting from the HashSet using:

    var b = vs["en-US"];

    Maybe using some HashSet extension?

Miguel Moura
  • 36,732
  • 85
  • 259
  • 481
  • Maybe you need a Dictionary instead of a HashSet – vc 74 Sep 23 '14 at 11:02
  • Yes, that would be an option but I would like to keep the Culture inside the VersionSetting object. – Miguel Moura Sep 23 '14 at 11:03
  • You can have it as both as the dictionary key and one of your classe's properties in which case you build your dictionary with versionSettings.ToDictionary(setting => setting.Culture) – vc 74 Sep 23 '14 at 11:04
  • Re your second question... you really want a dictionary. HashSets fundamentally don't let you retrieve their contents other than by iterating over them. – Rawling Sep 23 '14 at 11:27
  • @MDMoura: note that i've edited my answer since i've overlooked the second equestion until now. – Tim Schmelter Sep 23 '14 at 13:39

2 Answers2

6

You can override Equals and GetHashCode to not only compare references what Object.Equals does:

public class VersionSettings
{
    public String Culture { get; set; }
    public String Domain { get; set; }
    public String Name { get; set; }

    public override bool Equals(object obj)
    {
        VersionSettings other = obj as VersionSettings;
        if(other == null) return false;
        return Culture == other.Culture;
    }

    public override int GetHashCode()
    {
        return Culture == null ? 0 : Culture.GetHashCode();
    }
}

Another approach is to implement a custom IEqualityComparer<VersionSettings> which does not require to modify the class itself:

public class VersionSettingComparer : IEqualityComparer<VersionSettings>
{
    public bool Equals(VersionSettings x, VersionSettings y)
    {
        if (x == null && y == null) return true;
        if (x == null || y == null) return false;
        return x.Culture == y.Culture;
    }

    public int GetHashCode(VersionSettings obj)
    {
        if(obj == null) return 0;
        return obj.Culture == null ? 0 : obj.Culture.GetHashCode();
    }
}

Now you can use the constructor of HashSet<T>:

var vs = new HashSet<VersionSettings>(new VersionSettingComparer());

According to your second question how you can access the HashSet<VersionSetting> in this way:

var enUsSetting = vs["en-US"];

That doesn't work because a hashset has no indexer like a collection or dictionary. You probably want a Dictionary<string, VersionSettings> instead.

As simple but not that efficient workaround you can use LINQ:

var enUsSetting = vs.FirstOrDefault(x => x.Culture == "en-US");

If you want to create the mentioned dictionary you can also use LINQ:

Dictionary<string, VersionSettings> cultureLoookup = vs
    .ToDictionary(x => x.Culture, x => x);  // you only need to create this once

Now this works:

var enUsSetting = cultureLoookup["en-US"];
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Your `Equals` is broken syntactically. Also am not sure Why are you checking for `null` explicitly in `Equals`. – Sriram Sakthivel Sep 23 '14 at 11:11
  • 1
    @SriramSakthivel: why is it broken? I'm checking for `null` because it's possible that it's null. Either because the passed object is not a `VersionSettings` or because it's reference is `null`. In both cases they are not equal. – Tim Schmelter Sep 23 '14 at 11:14
  • Your equality comparer says that two null objects aren't equal. – Servy Sep 23 '14 at 14:02
  • @Servy: why should two null objects supposed to be equal? I have no informations about them so i refuse to treat them as if they were equal. But OP is free to change that implementation detail. **Edit** However, i'll change it since i've noticed that even `Object.Equals` does return true then. – Tim Schmelter Sep 23 '14 at 14:04
  • 3
    Well if no `null` objects are ever equal then you'll never be able to remove it from the set, ever. `Contains` on `null` will return `false`, even if there are (multiple) `null` items in the set, all of the superset/subset methods have the same problem. Basically all you can ever do is add the item to the set, and after that it'd only ever cause a big mess. – Servy Sep 23 '14 at 14:17
  • @Servy: that's a good point and explains why it seems to be a guideline in .NET to treat two null objects as equal. In sql `NULL` is never equal to something else (what was the reason for my implementation). But even VB.NET returns `False` for `Nothing <> NotNothing`. http://stackoverflow.com/q/15524045/284240 – Tim Schmelter Sep 23 '14 at 14:22
0

Why in a simple way you doesn't use Dictionary<string, VersionSettings>. For instace:

var settings = new Dictionary<string, VersionSettings>();
settings["en-US"] = usSettings;   
settings["es-ES"] = esSettings;
...
var currentSetting = settings[currentSettingIdText];

It is just a tip.

Raúl Otaño
  • 4,640
  • 3
  • 31
  • 65