基于泛型参数类型的类型推断(Delphi)

7

我正在尝试编写一个通用函数,它接受匹配的参数类型。
在普通参数的简单情况下,Delphi可以正确推断出类型参数。

例如:

type
  TFoo = class
    function Pair<T>(e1, e2: T): TList<T>;
  end;

使用aFoo.Pair(1, 2);调用是完全正常的,但是当我将参数签名更改为泛型类型时

type
  TFoo = class
    function InsertInto<T>(aList: TList<T>; aVal: T): TList<T>;
  end;

尝试调用它:
aFoo.InsertInto(TList<String>.Create, 'bar');

然后编译器报错:
E2010 不兼容的类型:'Generics.Collections.TList<uTest.TFoo.InsertInto.T>' 和 'Generics.Collections.TList<System.String>'

我是否有办法编写这个(或类似的)方法,以便客户端不必指定类型参数?
aFoo.InsertInto<String>(TList<String>.Create, 'bar');


我认为错误消息会给你一个提示,即类型推断在这里行不通。对于编译器的推断系统来说,这显然是一个更难的问题,比顶部问题的简单示例要困难得多。 - David Heffernan
尝试将aVal:T作为第一个参数,也许这样会更容易编译,但机会非常非常小。 - Arioch 'The
将“string”替换为“strign”并享受E2010不兼容类型:“System.Generics.Collections.TList<unitname.TFoo.Pair.T>”和“Boolean”。 - Arioch 'The
2
将第一个参数替换为nil,您将获得E2532 Couldn't infer generic type argument from different argument types for method 'Pair'//这可能是正确的错误,其他任何内容都是虚假的。我认为您可以就误导性错误消息开启QC工单。 - Arioch 'The
2
但是,@Arioch,我们不知道第二个函数的消息是否真的具有误导性,因为我们还没有理解编译器的问题。来自第一个函数的“无法推断泛型类型”错误是不相关的。对于第二个函数,编译器显然已经推断出了类型(因为它没有抱怨无法推断它们),所以问题实际上是要认识到推断出的类型与显式指定的类型相同——即uTest.TFoo.InsertInto.T等同于System.String——如果确实已经推断出了T - Rob Kennedy
@Rob,那是你的解释。我的解释是编译器在“取消别名”时失败了,就像它会直接将“:ParamName”传递给SQL服务器,而不实际进行符合SQL的替换。有点像空或垃圾引用。编译器应该用一些有效的类型替换uTest.TFoo.InsertInto.T占位符-但失败了,占位符仍然存在。否则,你如何解释我第二条评论中意外出现的“布尔值”? - Arioch 'The
1个回答

5

我猜这源于Delphi强类型的特性。
uTest.TFoo.InsertInto.T 相当于 System.String,但它实际上是不同的类型

就像在这个例子中,Int1Int2 不是同一类型:

var
  Int1: array[1..10] of Integer;
  Int2: array[1..10] of Integer;
      ...
  Int1 := Int2; // <== BOOM! E2008 Incompatible types (in XE2)

实际问题并非类型推断,而是类型不符合Pascal/Delphi的严格规则。

1
听起来非常有道理。只有一个小问题。你所谈论的是类型标识和兼容性,而不是强/弱类型系统。 - David Heffernan
2
它也可能是字符串字面量 'bar' 的类型不是 System.String —— 至少目前还不是。字符串字面量的类型相当流动;编译器会分配它需要具有的任何类型,至少在它知道需要什么类型时。想想同一个字符串字面量如何可以作为 UnicodeChar、PAnsiChar、WideString 或 ShortString 传递,所有这些都来自代码中的相同文本。因此,uTest.TFoo.InsertInto.T 实际上可能代表半打不同的类型,而编译器无法选择“最佳”类型。 - Rob Kennedy
它也不能与整型字面量一起使用,尽管这可能有类似的原因(Byte vs. Integer vs. Cardinal)。 - astrangeguy
@RobKennedy,也许你可以通过指向类型的指针来解决这个问题?我在Delphi7中遇到过类似的问题,但那并不是真正的泛型(模板),那时候通常的解决方案就是使用指针。 - Daniel B. Chapman
@Rob 如果你将 'bar' 文本替换为字符串类型的变量,消息仍然是相同的,因此它与常量的多类型性质无关。 - jachguate
1
我反对这个想法。原因一:整数数组(静态或动态)是不兼容的。但所有的TArray<Integer>都是相同的。而且有泛型TList<T>,所以应该遵循后者的特性,而不是前者。原因二:我非常希望看到type TFileName = type string;将字符串作为独立的子类型。我个人认为这既有用又符合type X = type Y背后的思想。但这不是Delphi处理字符串的方式。无论你做了什么类型声明,字符串始终是可赋值兼容的。 - Arioch 'The

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接