简体   繁体   English

字典C中的GetHashCode和Equals实现#

[英]GetHashCode and Equals implementation in Dictionary C#

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#. 我来到这个网站,在Dictionary中搜索对象比较,我开始知道重写GetHashCode和Equals是在C#中进行对象比较的必要条件。 Here is a piece of code that i have been trying to solve out, using FOREACH iteration Method. 这是我尝试使用FOREACH迭代方法解决的一段代码。 But my Boss says to do the same without using any iteration(maybe by using containskey or containsvalue method), due to performance issues. 但是由于性能问题,我的Boss说在不使用任何迭代(可能使用containskey或containsvalue方法)的情况下也这样做。 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();
        }

    }

For searching an object in a dictionary using the ContainsKey or ContainsValue keyword, compiler uses two implicit functions ie GetHashCode() and Equals(). 为了使用ContainsKey或ContainsValue关键字在字典中搜索对象,编译器使用两个隐式函数,即GetHashCode()和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

To search element in dictionary you can use ContainsKey, ContainsValue methods or just write LINQ query 要在字典中搜索元素,您可以使用ContainsKey,ContainsValue方法或只编写LINQ查询

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

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. Dictionary.ContainsKey(employee)在这里没有帮助,因为员工是“未知”值,而Contains将无济于事,因为它需要一个KeyValuePair<employee,manager>和......再次......没有员工知道。 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 ! ContainsValue(manager)将无法帮助,因为它不返回任何键因为它不是键,它是O(n)操作,而不是像ContainsKey这样的O(1)

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. 只有在manager获得有效的Equals实施后,这才有效。 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.) 因为不同的对象可能共享相同的哈希代码,所以只比较哈希代码不能替代Equals==CompareTo - 取决于哪一个是合适的。)

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. 这仅在manager具有有效的EqualsGetHashCode实现时才有效。 (GetHashCode is required because manager objects are used the key to the new Dictionary.) (GetHashCode是必需的,因为manager对象被用作新词典的键。)

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. 快乐的编码。

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> . 为了能够比较2个实例的相等性,您应该重写Equals方法,并且实现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). 当您重写Equals时,您还应该覆盖GetHashcode(当您将实例放入字典中以计算存储桶时使用此方法)。

You should not use GetHashcode yourselves in order to compare 2 instances of your object for equality; 你不应该使用GetHashcode来比较对象的2个实例是否相等; instead you should use Equals (or an EqualityComparer , which will use the Equals method as well). 相反,你应该使用Equals (或EqualityComparer ,它也将使用Equals方法)。

If you've implemented GetHashCode and Equals well, then you're able to determine whether a dictionary contains a specific instance by doing: 如果您已经很好地实现了GetHashCode和Equals,那么您可以通过执行以下操作来确定字典是否包含特定实例:

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

myDictionary.ContainsKey (someKey)

or 要么

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

I believe you have a bug in your final response. 我相信你的最终回复中有一个错误。

The line 这条线

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

Probably should be 可能应该是

return (this.GetHashCode() == otheremployee.GetHashCode()); 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. 正如您在回复中所写的那样,您似乎正在将另一个对象与自身进行比较。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM