IDictionary:使用自定义类作为键

发布于 2024-11-25 10:38:36 字数 1589 浏览 2 评论 0原文

我在我的一个项目中使用了 IDictionaryKeyClass 非常简单,仅包含两个整数。

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.

我在类 KeyClass 中实现了 IComparable 但它没有解决我的问题。我用谷歌搜索并找到了这篇 CodeProject 文章。它描述了使用实现 IEqualityComparer接口的类的以下技术。我在我的类中定义了它:

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 如下:

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

之后一切正常。

我的问题是,我在保存环境(.Net 4.0、Windows 7 桌面应用程序)中使用相同的类,并且 KeyClass 在另一个项目中没有 IEqualityComparet 实现的情况下工作,但为什么在我将课程放入这个项目后它就停止工作了?

I was using IDictionary<KeyClass, ValueClass> in one of my projects. The KeyClass is very simple containing just two integers.

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

I was accessing values from dictionary like follows:

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

The dictionary for sure contained KeyClass key with ValueA = 1 and ValueB = 1 and simillarly many other keys. But I got exception:

The given key is not present in the dictionary.

I implemented IComparable in class KeyClass but it did not solve my problem. I googled and found this CodeProject article. It describes following technique to use a class implementing IEqualityComparer<T> interface. I defined it in my class:

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;
        }
    }
}

Now I have to declare IDictionary<KeyClass, ValueClass> as follows:

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

Things worked fine afterwards.

My question is that I was using same class in save environment (.Net 4.0, Windows 7 desktop app) and KeyClass was working without IEqualityComparet<T> implementation in another project, but why it stopped working after I put the classes in this project?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

谜泪 2024-12-02 10:38:36

没有理由实现 IEqualityComparer。相反,重写KeyClass 中的GetHashCodeEquals 方法。

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,因此您可以在类中指定默认比较。

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

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

请参阅等于准则() 和运算符 ==

之前不起作用的原因可能是因为引用类型的默认比较是引用相等。因此,如果您写:

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

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

您将得到“找不到密钥”,因为“k”和“k1”不是同一个对象。但如果你写:

var v1 = d[k];

你会找到该值,因为 k 是你存储在字典中的键。

重写 Equals 可以解决这个问题。

There's no reason to implement IEqualityComparer<KeyClass>. Rather, override the GetHashCode and Equals methods in KeyClass.

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;
    }
}

You only need IEqualityComparer<KeyClass> if you're doing a custom comparison--something that's different from the default comparison. But since you control KeyClass, you can specify the default comparison in the class.

If you do it this way, then you don't have to supply a comparer when you create the dictionary. That is, you can write:

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

See Guidelines for Equals() and Operator ==.

The reason it didn't work before is probably because the default comparison for reference types is reference equality. So if you write:

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

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

You're going to get "key not found" because 'k' and 'k1' are not the same object. But if you wrote:

var v1 = d[k];

You'd find the value because k is the key that you stored in the dictionary.

Overriding Equals solves this problem.

野の 2024-12-02 10:38:36

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

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

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

在这种情况下,使用结构可能是最好的选择,但了解使用它们的含义很重要,您可以在此处阅读:
结构(C# 编程指南)

A class is a reference type - by default one reference is compared to another based on whether they point to the same instance of the class, not based on the values of its properties.

You'd be able to retrieve the item from the dictionary using the original instance you added the item with but not with some other instance that happens to have the same values for ValueA and ValueB.

If you want that behaviour you must either use a value type (a struct instead of a class), a custom comparer (as you have done) or you must override the Equals() and GetHashCode() methods of KeyClass.

Using a struct is probably the best option in this case but it's important to understand the implications of using them which you can read up on here:
Struct (C# Programming Guide)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文