IDictionary <t,k>:使用自定义类作为键</t,k>

时间:2011-07-20 18:32:54

标签: .net generics generic-collections

我在我的一个项目中使用IDictionary<KeyClass, ValueClass>KeyClass非常简单,只包含两个整数。

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}
}

我正在从字典中访问值,如下所示:

var k = new KeyClass(1, 1);
var v = myDictionary[k];

字典肯定包含KeyClass密钥,ValueA = 1ValueB = 1以及其他许多密钥。但我得到了例外:

The given key is not present in the dictionary.

我在课程IComparable中实施了KeyClass,但它没有解决我的问题。我用Google搜索了这篇CodeProject文章。它描述了使用实现IEqualityComparer<T>接口的类的以下技术。我在班上定义了它:

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}

    public class EqualityComparer : IEqualityComparer<KeyClass>
    {
        public bool Equals(KeyClass a, KeyClass b)
        {
            return a.ValueA == b.ValueA && a.ValueB == b.ValueB;
        }

        public int GetHashCode(KeyClass k)
        {
            return k.ValueA ^ k.ValueB;
        }
    }
}

现在我必须声明IDictionary<KeyClass, ValueClass>如下:

var d = new Dictionary<KeyClass, ValueClass>(new KeyClass.EqualityComparer());

之后事情很顺利。

我的问题是我在保存环境(.Net 4.0,Windows 7桌面应用程序)中使用相同的类,而KeyClass在另一个项目中没有IEqualityComparet<T>实现工作,但为什么它在之后停止工作我把这些课程放在这个项目中了吗?

2 个答案:

答案 0 :(得分:1)

没有理由实施IEqualityComparer<KeyClass>。而是覆盖GetHashCode中的EqualsKeyClass方法。

public class KeyClass
{
    public int ValueA{get;set;}
    public int ValueB{get;set;}

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        KeyClass p = obj as KeyClass;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return a.ValueA == b.ValueA && a.ValueB == b.ValueB;
    }

    public bool Equals(KeyClass p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return this.ValueA == p.ValueA && this.ValueB == b.ValueB;
    }

    public int GetHashCode(KeyClass k)
    {
        return k.ValueA ^ k.ValueB;
    }
}

如果您正在进行自定义比较,则只需要IEqualityComparer<KeyClass> - 这与默认比较不同。但是,由于您控制KeyClass,您可以在类中指定默认比较。

如果你这样做,那么在创建字典时你不必提供比较器。也就是说,你可以写:

var d = new Dictionary<KeyClass, ValueClass>();

See Guidelines for Equals() and Operator ==

之前没有工作的原因可能是因为引用类型的默认比较是引用相等。所以,如果你写:

var k = new KeyClass(1, 1);
var v = new ValueClass(..);
d.Add(k, v);

var k1 = new KeyClass(1, 1);
var v1 = d[k1];

你会得到&#34;没找到钥匙&#34;因为&#39; k&#39;和&#39;&#39;&#39;不是同一个对象。但如果你写了:

var v1 = d[k];

您找到了该值,因为k是您存储在字典中的密钥。

覆盖Equals解决了这个问题。

答案 1 :(得分:0)

一个类是一个引用类型 - 默认情况下,一个引用会根据它们是否指向同一个类的实例而不是基于其属性的值来与另一个引用进行比较。

您可以使用添加项目的原始实例从字典中检索项目,但不能使用恰好具有ValueA和ValueB相同值的其他实例来检索项目。

如果您想要这种行为,您必须使用值类型(结构而不是类),自定义比较器(如您所做),或者必须覆盖KeyClass的Equals()和GetHashCode()方法。 / p>

在这种情况下,使用结构可能是最好的选择,但了解使用它们的含义非常重要,您可以在这里阅读: Struct (C# Programming Guide)