这是泛型集合专家的问题。
我很震惊地发现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但不能使用字符串或整数,那么这也非常有用。
答案 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。