GetHashCode and Equals implementation in Dictionary C#

你。 提交于 2019-12-07 16:25:09

问题


I came to this site searching for object comparison in Dictionary, and i came to know that overriding GetHashCode and Equals are a must for doing object comparison in C#. Here is a piece of code that i have been trying to solve out, using FOREACH iteration Method. But my Boss says to do the same without using any iteration(maybe by using containskey or containsvalue method), due to performance issues. Any help is highly welcome..

  public class employee
    {
        public string empname { get; set; }
        public string location { get; set; }
        public double kinid { get; set; }
        public double managerKin { get; set; }
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }

    public class manager
    {
        public string managername { get; set; }
        public double kinid { get; set; }

        public override int GetHashCode() 
        { 
          return 17 * managername.GetHashCode() + kinid.GetHashCode();
        }
    }
    public class program
    {
        public static void Main()
        {
            employee emp = new employee();
            employee emp2 = new employee();
            manager mng = new manager();
            manager mng2 = new manager();

            emp.empname = "Deepak";
            emp.location = "Pune";
            emp.kinid = 36885;
            emp.managerKin = 007;


            emp2.empname = "Astha";
            emp2.location = "Pune";
            emp2.kinid = 30000;
            emp2.managerKin = 007;

            mng.kinid = 007;
            mng.managername = "Gaurav";
            mng2.kinid = 001;
            mng2.managername = "Surya";

            Dictionary<employee, manager> relations = new Dictionary<employee, manager>();
            relations.Add(emp, mng);
            relations.Add(emp2, mng2);

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("The Manager details are :");
            foreach (var element in relations)
            Console.WriteLine(" \n KINID : {0} \n  Manager'sName :                    {1}",element.Value.kinid, element.Value.managername);
            Console.WriteLine("Enter the details of the manager..");
            Console.ForegroundColor = ConsoleColor.Gray;
            Console.Write("\nManager's Kin : ");
            double mkin = Convert.ToDouble(Console.ReadLine());

            Console.Write("Manager's Name : ");
            string mname = Console.ReadLine();

            manager mng1 = new manager();
            mng1.kinid = mkin;
            mng1.managername = mname;
            int hashvalue = 17 * mname.GetHashCode() + mkin.GetHashCode();



            #region BY USING FOREACH LOOP
            int i = 0;
            foreach (var element in relations)
            {
                if (element.Value.GetHashCode() == hashvalue)
                {
                    i += 1;
                    if (i == 1)
                    {
                        Console.WriteLine("The Following employees report to the Manager : {0}", mname);

                    }
                    Console.WriteLine(element.Key.empname + " " + element.Key.kinid + " " + element.Key.location + " " + element.Key.managerKin);

                }
            }
            if (i == 0)
            {
                Console.WriteLine("sorry the manager's details you entered \"{0}\" \"{1}\" does not exist in our database..", mng1.managername, mng1.kinid);

            }
            #endregion

            Console.ReadLine();
        }

    }

回答1:


For searching an object in a dictionary using the ContainsKey or ContainsValue keyword, compiler uses two implicit functions i.e. GetHashCode() and Equals(). So when we have an object for comparison, we need to Override both these methods !!

Here is the code

#region USING DICTIONARY TO STORE CLASS OBJECTS (check employee existence and print manager's name)
public class employee
{
    public string empname { get; set; }
    public string location { get; set; }
    public double kinid { get; set; }
    public double managerKin { get; set; }

    //public override bool Equals(object obj) // ANY OF THE TWO EQUALS METHOD WORKS.
    //{
    //    employee otheremployee;
    //    otheremployee = (employee)obj;
    //    return (otheremployee.kinid == this.kinid && otheremployee.location == this.location && otheremployee.empname == this.empname && otheremployee.managerKin == this.managerKin);

    //}
    public override bool Equals(object obj)   //When Running this entire code, put a break-point on both the Equals() and GetHashCode() methods, and see the execution flow.
    {
        employee otheremployee;
        otheremployee = (employee)obj;
        return (obj.GetHashCode() == otheremployee.GetHashCode());

    }
    public override int GetHashCode()    //When Running this entire code, put a break-point on both the Equals() and GetHashCode() methods, and see the execution flow.
    {
        //int temp = base.GetHashCode(); // DONT USE THIS
        //return base.GetHashCode();
        int temp = empname.GetHashCode() + location.GetHashCode() + kinid.GetHashCode() + managerKin.GetHashCode();
        return temp;
    }
}

public class manager
{
    public string managername { get; set; }
    public double kinid { get; set; }



    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
}
public class program
{
    public static void Main()
    {
        employee emp = new employee();
        employee emp2 = new employee();
        manager mng = new manager();
        manager mng2 = new manager();

        emp.empname = "Deepak";
        emp.location = "Pune";
        emp.kinid = 36885;
        emp.managerKin = 007;


        emp2.empname = "Astha";
        emp2.location = "Pune";
        emp2.kinid = 30000;
        emp2.managerKin = 001;

        mng.kinid = 007;
        mng.managername = "Gaurav";
        mng2.kinid = 001;
        mng2.managername = "Surya";

        Dictionary<employee, manager> relations = new Dictionary<employee, manager>();
        relations.Add(emp, mng); // put a BreakPoint here and see the execution flow
        relations.Add(emp2, mng2);// put a BreakPoint here and see the execution flow

        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine("The Employee details are :");
        foreach (var element in relations)
            Console.WriteLine(" \n Employee Name : {0} \n Location : {1} \n Employee KinId : {2} \n Manager's KinId : {3} ",
                element.Key.empname, element.Key.location, element.Key.kinid, element.Key.managerKin);

        Console.WriteLine("Enter the details of the Employee..");
        Console.ForegroundColor = ConsoleColor.Gray;
        Console.Write("\nEmployee Name : "); string ename = Console.ReadLine();
        Console.Write("Location : "); string elocn = Console.ReadLine();
        Console.Write("Employee KinId : "); double ekinid = Convert.ToDouble(Console.ReadLine());
        Console.Write("Manager's ID : "); double emngr = Convert.ToDouble(Console.ReadLine());
        employee emp1 = new employee();
        emp1.empname = ename;
        emp1.location = elocn;
        emp1.kinid = ekinid;
        emp1.managerKin = emngr;


        int i = 0; // This variable acts as a indicator to find whether the Employee Key exists or not.
        if (relations.ContainsKey(emp1)) //Put a break point here and see the execution flow.
        {
            Console.WriteLine("the Employee : {0} exists..", emp1.empname);
            Console.WriteLine("the Employee reports to the following manager : {0} \n and the Manager's KinId is {1}.", (relations[emp1]).managername, relations[emp1].kinid);
            i = 1;
            Console.ReadLine();
        }

        if (i == 0)
        {
            Console.WriteLine("the details of the employee named {0} does not exist !!", emp1.empname);
            Console.ReadLine();
        }

#endregion



回答2:


To search element in dictionary you can use ContainsKey, ContainsValue methods or just write LINQ query

var dict = (from pair in relations
where pair.Value.Equals(mng1)
select pair).ToDictionary<employee,manager>();



回答3:


In order to be able to compare 2 instances for equality, you should override the Equals method and, it is also good practice to implement IEquatable<T>. When you override Equals, you should also override GetHashcode (this is used when you put your instances in a dictionary to calculate the bucket).

You should not use GetHashcode yourselves in order to compare 2 instances of your object for equality; instead you should use Equals (or an EqualityComparer, which will use the Equals method as well).

If you've implemented GetHashCode and Equals well, then you're able to determine whether a dictionary contains a specific instance by doing:

var myDictionary<int, Manager> = new Dictionary<int,Manager>();

myDictionary.ContainsKey (someKey)

or

var mySet = new HashSet<Manager>();
mySet.Contains(someManagerObject);



回答4:


Dictionary.ContainsKey(employee) won't help here because the employee is the "unknown" value, and Contains won't help because it takes a KeyValuePair<employee,manager> and ... once again ... no employee is known. ContainsValue(manager) won't help because it doesn't return any key and because it's not a key, it is an O(n) operation, not an O(1) like ContainsKey!

With the current structure the only way is with some form of looping, although I would write it like this:

// Key is Employee, Value is Manager
// This is O(n)
var theEmployees = relations
  .Where(rel => rel.Value.Equals(theManager))
  .Select(rel => rel.Key);

This will only work after manager is given a valid Equals implementation. Note that the hash code is not used at all. (Because objects that are different may share the same hash-code, just comparing the hash-code is not a replacement for Equals, or ==, or CompareTo! -- depending on which one is appropriate.)

If there will be many such queries then the initial structure can be "inverted".

// Build a reverse lookup-up
var employeesForManager = relations
  .GroupBy(rel => rel.Value)            // group on Manager
  .ToDictionary(g => g.Key, g => g);    // Key is the group's Manager

// This is O(1), but only valid AFTER employeesForManager is [re-]generated
var theEmployees = employeesForManager[theManager]

This will only work if manager has a valid Equals and GetHashCode implementation. (GetHashCode is required because manager objects are used the key to the new Dictionary.)

As for which is "better" -- well, that depends. It is silly to create the reverse-lookup to only use it once, for instance. There is no performance problem until there is a performance problem: write clean code and profile.

Happy coding.




回答5:


I believe you have a bug in your final response.

The line

return (obj.GetHashCode() == otheremployee.GetHashCode());

Probably should be

return (this.GetHashCode() == otheremployee.GetHashCode());

That way you are comparing the hash code for this object and the other object. As it is written in your response you appear to be comparing the other object to itself.



来源:https://stackoverflow.com/questions/8612973/gethashcode-and-equals-implementation-in-dictionary-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!