3

I have a SortedDictionary<Package, List<string>>. Following is the Package class:

using System;

namespace GetPackageInfo
{

public class Package : IComparable, IEquatable<Package> 
{
    public string PackageName;
    public string Version;

    public Package()
    {

    }

    public Package(string packageName)
    {
        this.PackageName = packageName;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int result = 17;
            result = result * 23 + ((PackageName != null) ? this.PackageName.GetHashCode() : 0);
            result = result * 23 + ((Version != null) ? this.Version.GetHashCode() : 0);
            return result;
        }
    }

    public bool Equals(Package other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }
        if (ReferenceEquals(this, other))
        {
            return true;
        }
        return Equals(this.PackageName, other.PackageName) &&
               Equals(this.Version, other.Version);
    }

    public override bool Equals(object obj)
    {
        Package temp = obj as Package;
        if (temp == null)
            return false;
        return this.Equals(temp);
    }

    public override string ToString()
    {
        return string.Format("PackageName: {0}, Version: {1}", PackageName, Version);
    }

    public int CompareTo(object obj)
    {
        if (obj == null)
            return 1;

        if (obj != null) 
            return (Equals(obj) ? -1 : 1);
        else
            throw new ArgumentException("Object is not a Temperature");
    }

Whenever I do a Contains or ContainsKey on the SortedDictionary, it does not work even when the name and version is the same.

if (nugetPackagesInfo.Keys.Any(p => p.Equals(package)))
{
    //List<string> currPackage;
    //nugetPackagesInfo.TryGetValue(package, out currPackage);
    if (!nugetPackagesInfo[package].Contains(packageFile))
    {
        nugetPackagesInfo[package].Add(packageFile);
    }

nuGetPackagesInfo is my dictionary object. The Any returns true though. But once it is passed and gets to nugetPackagesInfo[package], then it throws the KeyNotFoundException. Can you please help me figure it out? Is my CompareTo not correct?

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
  • *" Is my CompareTo not correct?"* Yes. It should return 0, <0 or >0 depending on the comparison. Your returns -1 when comparing equal objects. – Ivan Stoev Mar 17 '16 at 18:43

3 Answers3

2

Your implementation of CompareTo doesn't seem to be correct. In fact, you don't implement any ordering of packages. You should, most likely, order packages by name, and if equal by version.

The core of Package.CompareTo should look like this (simplified; not taking care of other == null):

// try name ordering
int nameOrdering = this.Name.CompareTo(other.Name);

// names not equal ⇒ ordering is clear and no need to inspect further
if (nameOrdering != 0)
{
    return nameOrdering;
}
// names are equal ⇒ resort to version ordering    
else
{
    return this.Version.CompareTo(other.Version);
}

You should also read the documentation for String.CompareTo() because of its culture-specific semantics.

Ondrej Tucny
  • 27,626
  • 6
  • 70
  • 90
1

Your CompareTo method should work as this:

  • return -1 if this is smaller than obj
  • return 1 if this is bigger than obj and
  • most important: return 0 if this equals obj
René Vogt
  • 43,056
  • 14
  • 77
  • 99
1

Change CompareTo and GetHashCode to these implementations.

public int CompareTo(object obj)
{
    if (obj == null)
        return 1;
    return this.ToString().CompareTo(obj.ToString());
}
public override int GetHashCode()
{
    unchecked
    {
        return ((PackageName != null ? PackageName.GetHashCode() : 0)*397) ^ (Version != null ? Version.GetHashCode() : 0);
    }
}

public override string ToString()
{
    return string.Format("PackageName: {0}, Version: {1}", PackageName??"", Version ?? "");
}

CompareTo - see the documentation. By using ToString() you get a comparison on the package name and then the version without having to do your own checks. Just make sure that ToString is correct (I added null check so it does not throw an Exception).

GetHashCode - not sure where you got your implementation from. You need to make sure that the hashcode is always unique for that item unless they truely are equal. This is the implementation I found on this previous answer on SO, see the last edit in the answer..

Community
  • 1
  • 1
Igor
  • 60,821
  • 10
  • 100
  • 175
  • Thanks a lot. It worked... I autogenerated the hashcode. May be that's what went wrong. Isnt autogeneration supposed to work ? – Shalima Sidhik Mar 17 '16 at 19:08
  • @ShalimaSidhik - Awesome! I am not sure I follow. How did you autogenerate the hashcode? Are you using a tool or maybe a built in feature in Visual Studio I am not aware of? – Igor Mar 17 '16 at 19:10
  • @ShalimaSidhik - By the way: If you deem an answer worthy as the answer to your question you can mark it so. There is a white check mark next to each answer, when you check it then it becomes green. You can do this in addition to the up/down votes that you deem worthy/unworthy as a response. The difference is that only one answer can be marked as the answer but you can up/down vote any/all answers. I write this because I saw on your profile that you have asked many previous questions but have never marked a response as an answer. – Igor Mar 17 '16 at 19:26
  • Thanks a lot. I never noticed it. – Shalima Sidhik Mar 23 '16 at 04:02