Delphi泛型集合是否覆盖等于?

时间:2010-07-30 01:49:02

标签: delphi

这是泛型集合专家的问题。

我很震惊地发现TList没有覆盖等于。看一下这个例子:

list1:=TList<String>.Create;       
list2:=TList<String>.Create;

list1.Add('Test');
list2.Add('Test');

Result:=list1.Equals(list2);

“结果”为false,即使两个列表包含相同的数据。它使用默认的equals()(只比较两个引用的相等性)。

查看代码,对所有其他通用集合类型看起来也是如此。

这是对的,还是我错过了什么?

如果在实践中尝试使用TLists,这似乎是个大问题。我该如何解决这个问题?我是否创建了自己的TBetterList来扩展TList并覆盖等于做一些有用的事情? 或者我会遇到Delphi泛型的进一步复杂化......?

[编辑:到目前为止我有一个答案,有很多赞成票,但它并没有真正告诉我我想知道什么。我会尝试重新解释这个问题]

在Java中,我可以这样做:

List<Person> list1=new ArrayList<Person>();
List<Person> list2=new ArrayList<Person>();
list1.add(person1);
list2.add(person1);
boolean result=list1.equals(list2);

结果 true 。我没有必要继承任何东西,它只是有效。

如何在Delphi中执行等效操作?

如果我在Delphi中编写相同的代码,结果将结束 false。

如果有一个解决方案只适用于TObject但不能使用字符串或整数,那么这也非常有用。

3 个答案:

答案 0 :(得分:8)

泛型与这个问题的关键并不直接相关:选择构成 Equals()测试的有效基本实现的内容完全是任意的。 TList.Equals()的当前实现至少是一致的(我认为)VCL中的所有其他类似的基类,并且通过类似的我不仅仅意味着集合或泛型类。

例如, TPersistent.Equals()也进行简单的引用比较 - 它不会比较任何已发布属性的值,这可能是您所拥有的等式测试类型的语义等价物记住 TList

你谈到扩展 TBetterList 并在派生类中做一些有用的事情,好像这对你来说是一项繁重的义务,但这是面向对象软件开发的本质。

核心框架中的基类提供了通用实用程序定义的内容。您认为 Equals()的有效实现可能与其他人的需求(或者在您自己的项目中从一个从该基类派生的类到另一个类)有很大不同。

所以,是的,由您决定实现对提供的基类的扩展,然后提供一个对您特别有用的 new 基类。

但这不是问题。

这是一个机会。

:)

然而,你肯定会遇到泛型的更多问题,而不仅仅是在Delphi中。 ;)

答案 1 :(得分:3)

归结为:

在Java(和.NET语言)中,所有类型都来自Object。在Delphi中,整数,字符串等不会从TObject下降。它们是本机类型,没有类定义。

这种差异的含义有时是微妙的。在泛型集合的情况下,Java可以假设任何类型都有Equals方法。因此,编写Equals的默认实现只需迭代两个列表并在每个对象上调用Equals方法。

从Java 6中的AbstractList定义打开JDK:

public boolean equals(Object o) {
    if (o == this)
        return true;
    if (!(o instanceof List))
        return false;

    ListIterator<E> e1 = listIterator();
    ListIterator e2 = ((List) o).listIterator();
    while(e1.hasNext() && e2.hasNext()) {
        E o1 = e1.next();
        Object o2 = e2.next();
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }
    return !(e1.hasNext() || e2.hasNext());
}

正如你所看到的那样,默认的实现并不是真的那么深。您仍然会覆盖Equals来比较更复杂的对象。

在Delphi中,因为T的类型不能保证是一个对象,所以Equals的默认实现不起作用。因此,Delphi的开发人员无可奈何地向应用程序开发人员重写TObject.Equals

答案 2 :(得分:1)

我环顾四周,在DeHL(一个开源的Delphi库)中找到了解决方案。 DeHL有一个Collections库,有自己的替代List实现。在询问开发人员之后,比较通用TList的能力被添加到当前不稳定版本的DeHL中。

因此,此代码现在将为我提供我正在寻找的结果(在Delphi中):

list1:=TList<Person>.Create([Person.Create('Test')]);
list2:=TList<Person>.Create([Person.Create('Test')]);

PersonsEqual:=list1.Equals(list2); // equals true

适用于所有类型,包括String和Integer类型

stringList1:=TList<string>.Create(['Test']);
stringList2:=TList<string>.Create(['Test']);

StringsEqual:=stringList1.Equals(stringList2); // also equals true

甜!

您需要查看DeHL的最新不稳定版本(r497)才能使其正常工作。当前稳定版本(0.8.4)与标准Delphi TList具有相同的行为。

请注意,这是最近的更改,可能无法进入DeHL的最终API(我当然希望它能够实现)。

那么也许我会使用DeHL而不是标准的Delphi集合?这是一种耻辱,因为我喜欢使用标准平台库。我将进一步研究DeHL。