(delphi11最新学习资料) Object Pascal 学习笔记---第14章泛型第3节(特定类约束)

14.3.2 特定类约束

​ 如果您的泛型类需要使用某个特定子集的类(特定层次结构),则可能需要根据给定基类指定约束。

​ 例如,如果您声明:

type
	TCompClass<T: TComponent> = class

​ 则此泛型类的实例仅适用于组件类,即任何TComponent后代类。这使您拥有一个非常特定的泛型类型(是的,听起来很奇怪,但这确实是它的实际情况),并且编译器将允许您在处理泛型类型时使用TComponent类的所有方法。

​ 如果这看起来非常强大,那么请三思。如果你考虑一下利用继承和类型兼容规则可以实现的功能,也许你可以使用传统的面向对象技术来解决同样的问题,而不必使用泛型类。我并不是说特定的类约束从来都没有用,但它肯定没有更高级别的类约束或(我觉得非常有趣的)基于接口的约束强大。

14.3.3 接口约束

​ 一般来说,更灵活的做法是只接受实现特定接口的类作为类型参数,而不是将一个泛型类约束为一个给定的类。这样就可以在泛型的实例上调用接口。在 C# 语言中,将接口约束用于泛型也很常见。让我先展示给你一个示例(来自IntfConstraint示例)。首先,我们需要声明一个接口:

type
  IGetValue = interface
    ['{60700EC4-2CDA-4CD1-A1A2-07973D9D2444}']
    function GetValue: Integer;
    procedure SetValue(Value: Integer);
    property Value: Integer read GetValue write SetValue;
  end;

​ 接下来,我们可以定义一个实现它的类:

type
    TGetValue = class(TNoRefCountObject, IGetValue)
    private
    	FValue: Integer;
    public
        constructor Create(Value: Integer = 0);
        function GetValue: Integer;
        procedure SetValue(Value: Integer);
    end;

​ 在限制为实现特定接口的类型的泛型类的定义中,事情开始变得有趣:

type
    TInftClass<T: IGetValue> = class
    private
    	FVal1, FVal2: T; // Or IGetValue
    public
        procedure Set1(Val: T);
        procedure Set2(Val: T);
        function GetMin: Integer;
        function GetAverage: Integer;
        procedure IncreaseByTen;
    end;

​ 请注意,在这个类的泛型方法的代码中,我们可以编写:

function TInftClass<T>.GetMin: Integer;
begin
	Result := Min(FVal1.GetValue, FVal2.GetValue);
end;

procedure TInftClass<T>.IncreaseByTen;
begin
    FVal1.SetValue(FVal1.GetValue + 10);
    FVal2.Value := FVal2.Value + 10;
end;

​ 有了所有这些定义,我们现在可以按以下方式使用泛型类:

procedure TFormIntfConstraint.BtnValueClick(Sender: TObject);
var
	IClass: TInftClass<TGetValue>;
begin
    IClass := TInftClass<TGetValue>.Create;
    try
        IClass.Set1(TGetValue.Create(5));
        IClass.Set2(TGetValue.Create(25));
        Show('Average: ' + IntToStr(IClass.GetAverage));
        IClass.IncreaseByTen;
        Show('Min: ' + IntToStr(IClass.GetMin));
    finally
        IClass.FVal1.Free;
        IClass.FVal2.Free;
        IClass.Free;
    end;
end;

​ 为了展示这个泛型类的灵活性,我为接口创建了另一个完全不同的实现方法:

type
TButtonValue = class(TButton, IGetValue)
public
  function GetValue: Integer;
  procedure SetValue(Value: Integer);
  class function MakeTButtonValue(Owner: TComponent; Parent: TWinControl): TButtonValue;
end;

function TButtonValue.GetValue: Integer;
begin
  Result := Left; // use base class property
end;

procedure TButtonValue.SetValue(Value: Integer);
begin
  Left := Value; // use base class property
end;

​ 该类函数(此处未显示)在父控件中创建了一个随机位置的按钮。位置创建一个按钮,并在以下示例代码中使用:

procedure TFormIntfConstraint.BtnValueButtonClick(Sender: TObject);
var
  IClass: TInftClass<TButtonValue>;
begin
  IClass := TInftClass<TButtonValue>.Create;
  try
    IClass.Set1(TButtonValue.MakeTButtonValue(Self, ScrollBox1));
    IClass.Set2(TButtonValue.MakeTButtonValue(Self, ScrollBox1));
    Show('Average: ' + IntToStr(IClass.GetAverage));
    Show('Min: ' + IntToStr(IClass.GetMin));
    IClass.IncreaseByTen;
    Show('New Average: ' + IntToStr(IClass.GetAverage));
  finally
    IClass.Free;
  end;
end;

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-08 18:04:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-08 18:04:03       101 阅读
  3. 在Django里面运行非项目文件

    2024-06-08 18:04:03       82 阅读
  4. Python语言-面向对象

    2024-06-08 18:04:03       91 阅读

热门阅读

  1. Android SplashActivity runs twice at launch on Android 13 API 33

    2024-06-08 18:04:03       26 阅读
  2. C#面:解释什么是闭包

    2024-06-08 18:04:03       30 阅读
  3. 使用Python编写Ping监测程序

    2024-06-08 18:04:03       31 阅读
  4. h5 拍照后压缩图片上传 方法直接用

    2024-06-08 18:04:03       35 阅读
  5. 系统与软件工程软件测试设计技术

    2024-06-08 18:04:03       32 阅读