【问题标题】:Would templates (or other technology) support the following construct?模板(或其他技术)是否支持以下构造?
【发布时间】:2010-10-23 18:15:45
【问题描述】:

这是我 other question 的后续跟进。

当我第一次听说泛型时,是在 Delphi 2009 发布之前(他们首先介绍了泛型)。我知道在此之前它在 .Net 中得到了支持,但我还没有深入到那个领域。

阅读泛型时,我了解到它允许类有一个变量参数,并且你传递给它的任何值都会被类的所有代码替换。

根据以下声明,描述泛型的方式(或者至少我理解的泛型允许的方式)是这样的:

procedure TMyClass<T>.Init;
begin
  FField := T.Create(nil);
end;

我认为它会起作用。我假设编译失败的地方如下:

begin
  TMyClass<TComponent>.Create; //Works correctly
  TMyClass<TObject>.Create;  //Doesn't work, as even though it HAS a constructor, it has none that receive a single pointer parameter
  TMyClass<string>.Create; //Doesn't work, not an object. 
end;

现在,我很清楚我错了。所以,我现在想知道的是,是否有支持这种结构的技术/语言特性。也许是代码模板?其他编程语言中的泛型?还是别的什么?

【问题讨论】:

    标签: delphi templates generics language-features


    【解决方案1】:

    现在,我很清楚我错了。所以呢 我现在想知道,有没有 技术/语言功能,将 支持这样的结构。代码 也许是模板?其他的泛型 编程语言?或许 还有什么?

    C# 中的泛型具有您想要的功能。 C++ 中的模板更加强大——通过模板生成的代码与手工编写的代码相同,除了它们只能内联编译的部分,这很糟糕。

    【讨论】:

    • 如果我错了,请纠正我,但是 C++ 中的模板的缺点是在使用它的单元的范围内被“定义”。因此单元 A.TMyClass 将与单元 B.TMyClass 不兼容。对吗?
    • 在 .NET 中,IL 级别支持泛型和统一类型系统(包括值类型在内的所有内容),因此 .NET 平台上的几乎所有语言(包括 Delphi Prism、VB.NET 和许多其他)都支持,而不仅仅是 C#。
    • @Ken:你错了。 @Jeroen:这是真的。 C# 确实有泛型,所以我没有错,但我本可以更正确。
    • 好的,回去看我读过的文章,不是单元之间,而是组件之间不兼容......我的错。 (供参考:developer.com/net/net/article.php/3367531/…
    • @Ken:C++ 没有程序集。模板类型只有在具有不兼容定义的情况下才在任何事物之间不兼容。模板在 C++ 中并不特殊——它们与常规类不兼容或不兼容,除非您必须提供源代码。
    【解决方案2】:

    @Gamecat,您不能将 TObject 作为约束,但可以将 class 作为约束(这很好地弥补了 TObject 约束的缺失)。

    请注意,无论您使用TObject还是class,都不能毫无技巧地使用参数调用Create

    示例 1:class 约束:

    unit Unit1;
    
    interface
    
    uses
      Classes;
    
    type
      TMyClass<T: class, constructor> = class
      strict private
        FField: T;
      public
        procedure Init;
      end;
    
    implementation
    
    procedure TMyClass<T>.Init;
    begin
      FField := T.Create();
    end;
    
    end.
    

    示例2:TComponent作为约束,Create中的参数

    unit Unit2;
    
    interface
    
    uses
      Classes;
    
    type
      TMyClass<T: TComponent, constructor> = class
      strict private
        FField: T;
      public
        procedure Init;
      end;
    
    implementation
    
    procedure TMyClass<T>.Init;
    var
      ComponentClass: TComponentClass;
    begin
      ComponentClass := T;
      FField := ComponentClass.Create(nil);
    end;
    
    end.
    

    除了class 约束之外,您还可以有一个记录约束。 这样,您需要 Default 来初始化字段:

    unit Unit3;
    
    interface
    
    uses
      Classes;
    
    type
      TMyClass<T: record> = class
      strict private
        FField: T;
      public
        procedure Init;
      end;
    
    implementation
    
    procedure TMyClass<T>.Init;
    begin
      FField := Default(T);
    end;
    
    end.
    

    希望能对泛型和约束有所了解。

    --杰罗恩

    【讨论】:

    • 我知道,但我看到(为时已晚)问题不在于如何让它工作,而是是否有允许构造的语言(见最后一段)。
    • +1 为 ComponentClass.Create(nil);诡计。这真的让我很开心。非常好的解决方法是缺少适当的(或更进化的)构造函数约束。
    • @Gamecat:我想知道原因,感谢澄清。不用担心,我自己通常不是一个“好的问题阅读者”:-) - 请注意,我输入了两个答案来解决大部分问题。
    【解决方案3】:

    您可以对泛型类型施加约束。如果你想使用该类型的某些方面,你需要这个。例如一个方法。

    如果要调用构造函数,需要在类约束旁边给出构造函数约束:

    type
      TMyClass<T: TComponent, constructor> = class
        // ..
      end;
    
    procedure TMyClass<T>.Init;
    begin
      FField := T.Create(nil);
    end;
    

    不幸的是,TObject 不是一个有效的约束。 (根据 Delphi XE)。

    现在,我很清楚我错了。所以,我现在想知道的是,是否有一种技术/语言>功能可以支持这种结构。也许是代码模板?其他>编程语言中的泛型?或者也许是别的什么? 这可能是有风险的,甚至是毫无意义的。如果您在泛型上调用方法 X 并使用不支持方法 X 的类对其进行实例化,那么正确的行为是什么...

    【讨论】:

    • 构造函数约束只允许调用无参数构造函数。即使将 TComponent 设置为约束也不允许调用 TComponent 的构造函数,因为它有 1 个参数。
    【解决方案4】:

    @Ken:为了让您要求的代码以真正的通用方式工作,您需要有一个统一的类型系统,它可以合并引用类型(类)和值类型(字符串、整数等)。

    从历史上看,原生 Delphi 没有这样的类型系统(.NET 有,并且 Delphi Prism 中的泛型支持它,就像 C# 和 VB.NET 一样)。

    解决这个问题很困难; Allen Bauer gave it a shot implementing a Nullable type,他不得不做一些严重的扭曲,以涵盖引用和值类型的方式仅实现 Equals (=) 和 NotEquals () 运算符行为。

    所以支持这些将是艰难的,但可能是可行的:

    begin
      TMyClass<TComponent>.Create; //Works correctly
      TMyClass<TObject>.Create;  //Doesn't work, as even though it HAS a constructor, it has none that receive a single pointer parameter
      TMyClass<string>.Create; //Doesn't work, not an object.
    end;
    

    --杰罗恩

    【讨论】:

      最近更新 更多