代码之家  ›  专栏  ›  技术社区  ›  Nick Gotch

这段代码是否依赖字符串实习来工作?

  •  4
  • Nick Gotch  · 技术社区  · 15 年前

    我正在为一个由两个字符串组成的字典创建一个键。当我在一个控制台应用程序中测试这个方法时,它是有效的,但我不确定它有效的唯一原因是否是因为字符串被插入,因此具有相同的引用。

    Foo foo1 = new Foo();
    Foo foo2 = new Foo();
    foo1.Key1 = "abc";
    foo2.Key1 = "abc";
    foo1.Key2 = "def";
    foo2.Key2 = "def";
    
    Dictionary<Foo, string> bar = new Dictionary<Foo, string>();
    bar.Add(foo1, "found");
    
    if(bar.ContainsKey(foo2))
        System.Console.WriteLine("This works.");
    else
        System.Console.WriteLine("Does not work");
    

    public struct Foo
    {
        public string Key1;
        public string Key2;
    }
    

    是否有任何情况会导致此失败,或者我是否可以将此作为唯一密钥?

    5 回复  |  直到 15 年前
        1
  •  4
  •   rui    15 年前

    根据Microsoft的文档,如果您打算将自己的数据结构用作哈希表中的键,则应始终覆盖GetHashCode(),否则可能会不安全。

    “在哈希表对象中用作键的对象也必须重写GetHashCode方法,因为这些对象必须生成自己的哈希代码。”

    http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx

        2
  •  4
  •   Community CDub    8 年前

    根据我对 rubim answer

    public struct Foo
    {
        private readonly string key1;
    
        private readonly string key2;
    
        public string Key1
        {
            get
            {
                return this.key1;
            }
        }
    
        public string Key2
        {
            get
            {
                return this.key2;
            }
        }
    
        public Foo(string key1, string key2)
        {
            this.key1 = key1;
            this.key2 = key2;
        }
    
        public static bool operator ==(Foo foo1, Foo foo2)
        {
            return foo1.Equals(foo2);
        }
    
        public static bool operator !=(Foo foo1, Foo foo2)
        {
            return !(foo1 == foo2);
        }
    
        public override bool Equals(object obj)
        {
            if (!(obj is Foo))
            {
                return false;
            }
    
            Foo foo = (Foo)obj;
            bool key1Equal = ((this.key1 == null) && (foo.Key1 == null))
                || ((this.key1 != null) && this.key1.Equals(foo.Key1));
            bool key2Equal = ((this.key2 == null) && (foo.Key2 == null))
                || ((this.key2 != null) && this.key2.Equals(foo.Key2));
    
            return key1Equal && key2Equal;
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                int hash = 17;
    
                hash = (23 * hash)
                    + (this.key1 == null ? 0 : this.key1.GetHashCode());
                return (31 * hash)
                    + (this.key2 == null ? 0 : this.key2.GetHashCode());
            }
        }
    
        public override string ToString()
        {
            return (this.key1 == null ? string.Empty : this.key1.ToString() + ",")
                + (this.key2 == null ? string.Empty : this.key2.ToString());
        }
    }
    

        Foo foo1 = new Foo("abc", "def");
        Foo foo2 = new Foo("abc", "def");
    
        Dictionary<Foo, string> bar = new Dictionary<Foo, string>();
        bar.Add(foo1, "found");
    
        if (bar.ContainsKey(foo2))
        {
            Console.WriteLine("This works.");
        }
        else
        {
            Console.WriteLine("Does not work");
        }
    
        3
  •  3
  •   hackerhasid    15 年前

    http://msdn.microsoft.com/en-us/library/dd183755.aspx

    您定义的任何结构都已经有一个默认的值相等实现,它继承自对象..::.Equals(Object)方法的系统..::.ValueType重写。此实现使用反射检查类型中的所有公共和非公共字段和属性。尽管此实现生成正确的结果,但与专门为该类型编写的自定义实现相比,它的速度相对较慢。

        4
  •  1
  •   Community CDub    4 年前

    根据MSDN,

    GetHashCode方法可以由派生类型重写。 提供适合该类型的哈希函数,并在哈希表中提供有用的分布。

    因此,您的代码不能保证工作,如果它工作,就不能保证在下一版本的.NET framework中继续工作。

    不管它的价值是什么,Monos当前的 ValueType.GetHashCode 调用类型成员 GetHashCode 实际上,它可以独立于字符串实习而正常工作。

        5
  •  0
  •   David Corbin    15 年前

    您的代码在.NET中应该可以正常工作,而且我敢说,即使.NET版本发生变化,它也会保持不变。问题是dictionary使用两种方式通过特定键访问值:Equals()和GetHashCode(),方式如下

    • 通过GetHashCode()值获取与请求的键值匹配的所有键(对于相等的对象,GetHashCode值必须相等,对于不同的对象,GetHashCode值应该不同)
    • 使用Equals()筛选出上一步中创建的列表。

    值类型的Standard Equals()使用反射来访问字段并进行比较(如前所述)。考虑到这一点,默认的GetHashCode()必须与默认的Equals()实现的工作方式保持一致——如果a==b,a.GetHashCode()==b.GetHashCode()——无论哪种方式,如果它甚至不满足所需的最小值,那么提供默认实现就毫无意义。MSDN说的是GetHashCode()不提供唯一的值,这是完全可以理解的。请记住,这种简单的实现:

    public int GetHashCode()
    {
       return 0;
    }
    

    即使值不是唯一的,也仍然正确(字典、哈希表等可以使用)。性能当然是另一回事,使用这种实现将导致对每个集合进行完全扫描,并测试每个元素是否相等。