Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么C#字典不调用隐藏的GetHashCode方法_C#_Dictionary - Fatal编程技术网

为什么C#字典不调用隐藏的GetHashCode方法

为什么C#字典不调用隐藏的GetHashCode方法,c#,dictionary,C#,Dictionary,我在一个项目中广泛使用了通用C#。我需要复合键,所以我一直使用as键。在某个时候,我想知道使用一个缓存哈希代码的自定义类是否有益: public class CompositeKey<T1, T2> : Tuple<T1, T2> { private readonly int _hashCode; public CompositeKey(T1 t1, T2 t2) : base(t1, t2) { _hashCode = base

我在一个项目中广泛使用了通用C#。我需要复合键,所以我一直使用as键。在某个时候,我想知道使用一个缓存哈希代码的自定义类是否有益:

public class CompositeKey<T1, T2> : Tuple<T1, T2>
{
    private readonly int _hashCode;

    public CompositeKey(T1 t1, T2 t2) : base(t1, t2)
    {
        _hashCode = base.GetHashCode();
    }

    public new int GetHashCode()
    {
        return _hashCode;
    }
}
然而,我注意到我的自定义
GetHashCode
方法根本没有被调用。当我将
new
更改为
override
时,它按预期被调用

有人能解释为什么不调用隐藏的
GetHashCode
方法吗?如果我像这样定义字典,我会期待这种行为

var dict = new Dictionary<Tuple<string, int>, int>();
var dict=newdictionary();
但如果我像我的示例中那样显式地指定
CompositeKey
类型,则不会


另外,我知道隐藏
GetHashCode
方法可能不是一个好主意。

泛型类型中方法调用的重载解析发生在编译未绑定泛型类型(例如
Dictionary
)时,而不是构造封闭类型(例如
Dictionary
)时

由于
Dictionary
中对
TKey
没有约束,因此可用的
GetHashCode()
的唯一重载是
object.GetHashCode()
。构造一个具有更好重载的
GetHashCode()
类型不会更改初始重载解析

它不仅限于使用
new
隐藏的方法。重载方法也会发生同样的情况:

class Generic<T>
{
    public bool Equal(T t1, T t2)
    {
        return t1.Equals(t2);
    }
}

class X : IEquatable<X>
{
    public override bool Equals(object obj)
    {
        Console.WriteLine("object.Equals");
        return true;
    }

    public bool Equals(X other)
    {
        Console.WriteLine("IEquatable.Equals");
        return true;
    }
}
有人能解释为什么不调用隐藏的GetHashCode方法吗? 如果我将字典定义为 这个

为了能够调用
CompositeKey.GetHashCode
方法,必须在编译时将
CompositeKey
实例的引用键入为
CompositeKey

但是
字典
的代码库不知道您的
复合键
类(显然)。它所知道的只是
TKey
(泛型类型参数),它与没有任何约束的
System.Object
一样等效。因为除了在
System.Object
中声明的方法之外,您不能调用
t
的任何方法,而无约束


因此,Dictionary最终调用了类中未重写的
Object.GetHashCode
,因此它没有被调用。

这是因为泛型上的类型约束。下面是一个简化的程序来说明这个问题

public class Program
{
    public static void Main(string[] args)
    {
        var bar = new Bar();
        TestMethod(bar);
        TestMethod2(bar);
    }

    public static void TestMethod<T>(T obj) where T : Foo
    {
        obj.Test();
        obj.Test2();
    }

    public static void TestMethod2<T>(T obj) where T : Bar
    {
        obj.Test();
        obj.Test2();
    }
}

public class Foo
{
    public virtual void Test()
    {
        Debugger.Break();
    }

    public virtual void Test2()
    {
        Debugger.Break();
    }
}

public class Bar : Foo
{
    public new void Test()
    {
        Debugger.Break();
    }

    public override void Test2()
    {
        Debugger.Break();
    }
}
现在,关于您的问题,正在使用的比较器是


这就是为什么函数只在您重写它时才被调用,而在您隐藏它时才被调用。

仅详细说明前面的答案。new的问题在于,只有当使用者直接操作该类(在本例中是您的CompositeKey类)时,它才会覆盖该方法。对CompositeKey派生自的任何基类的任何调用都不会调用您的新成员

因此,如果在以下情况下:


  • 隐藏是个坏主意,因为它会导致这个问题!这里有一篇文章解释了“新建”和“覆盖”之间的区别。。。另一方面,如果你认为你的hashcode算法需要缓存,那可能是一个糟糕的算法,或者你缓存了不必要的东西。没错,这肯定是一个微观优化,但我仍然好奇它是否会产生可测量的差异。在我的测试用例中,
    CompositeKey
    甚至比
    元组
    的性能稍差(当使用
    覆盖
    和缓存的哈希代码时)。@AndyJ我知道隐藏通常是个坏主意,在这种情况下我永远不会使用它。但是假设有人向我展示了我的例子,并问我在这种特定情况下使用
    new
    而不是
    override
    是否会有所不同,我会说不。但我想你每天都会学到一些新东西:)谢谢你的例子。我不知道这些约束除了在您试图构造泛型类型的新实例时进行类型检查之外,还有其他任何影响。
    var test = new Generic<X>();
    test.Equal(new X(), new X()); 
    
    // prints "object.Equals"
    
    public class Program
    {
        public static void Main(string[] args)
        {
            var bar = new Bar();
            TestMethod(bar);
            TestMethod2(bar);
        }
    
        public static void TestMethod<T>(T obj) where T : Foo
        {
            obj.Test();
            obj.Test2();
        }
    
        public static void TestMethod2<T>(T obj) where T : Bar
        {
            obj.Test();
            obj.Test2();
        }
    }
    
    public class Foo
    {
        public virtual void Test()
        {
            Debugger.Break();
        }
    
        public virtual void Test2()
        {
            Debugger.Break();
        }
    }
    
    public class Bar : Foo
    {
        public new void Test()
        {
            Debugger.Break();
        }
    
        public override void Test2()
        {
            Debugger.Break();
        }
    }
    
    public static void TestMethod<T>(T obj)
    {
        ((Foo)obj).Test(); //You would expect this to call Foo.Test() b\c of shadowing
        ((Foo)obj).Test2(); //You would expect this to call Bar.Test2() b\c of overloading
    }
    
    [Serializable]
    internal class ObjectEqualityComparer<T>: EqualityComparer<T>
    {
        [Pure]
        public override bool Equals(T x, T y) {
            if (x != null) {
                if (y != null) return x.Equals(y);
                return false;
            }
            if (y != null) return false;
            return true;
        }
    
        [Pure]
        public override int GetHashCode(T obj) {
            if (obj == null) return 0;
            return obj.GetHashCode();
        }
        //...
    }
    
        public override bool Equals(T x, T y) {
            if (x != null) {
                if (y != null) return ((object)x).Equals(y);
                return false;
            }
            if (y != null) return false;
            return true;
        }
    
        [Pure]
        public override int GetHashCode(T obj) {
            if (obj == null) return 0;
            return ((object)obj).GetHashCode();
        }