我可以将泛型限制(编译或运行时)为 char 数组 [0..n]

Can I restrict (compile or runtime) a generic to being an array [0..n] of char

我有一个程序,其中有很多结构定义为 char 和记录的静态数组(通常由 char 数组组成,但这并不重要)。

我正在尝试为这些结构创建通用接口,以便将它们传递给后端 C DLL。

我可以使用 <T: record> 约束处理所有类型的记录,但是 array[0..n] of char 违反了 'non-nullable value type' 规则。

我可以通过为我的不同静态数组声明类型来使用不受约束的泛型(TMyArray = array[0..5] of char - 这很好,因为它已经存在于现有代码中),但我需要失去我的 <T: record> 约束.由于我的代码不适用于 classes 或动态数组,我希望能够将 T 限制为记录或静态数组 char.

我可以使用和不使用 record 约束的两个构造函数。然后我可以使用旧式 RTTI 来测试不受约束的类型是否为数组:

var 
  l: PTypeInfo;
begin
  l := TypeInfo(T);
  assert(l.Kind = tkArray);
end;

但我不认为我可以检查包含的类型是一个字符吗? 2010 RTTI 抱怨 T 必须是 class,所以我认为这也不是一个问题。 我可以通过创建仅包含我的静态字符数组的记录来稍微解决这个问题,但这感觉有点像软糖,而且在代码中看起来很笨拙。

这是一个编译器缺陷。固定长度数组是不可为空的值类型。该缺陷仍然存在于 Delphi 10 西雅图。该程序编译失败:

{$APPTYPE CONSOLE}

type
  TFoo = record
    class procedure Bar<T: record>(const arg: T); static;
  end;

class procedure TFoo.Bar<T>(const arg: T);
begin
end;

type
  TArr = array [0..0] of char;

var
  Arr: TArr;

begin
  TFoo.Bar<TArr>(Arr);
end.

错误是:

[dcc32 Error] E2512 Type parameter 'T' must be a non-nullable value type

所以,我想您将不得不使用 RTTI 通过运行时检查来处理这个问题。你当然可以做到。该程序演示:

{$APPTYPE CONSOLE}

uses
  Rtti, TypInfo;

type
  TFoo = record
    class procedure Bar<T>(const arg: T); static;
  end;

class procedure TFoo.Bar<T>(const arg: T);
var
  TypInfo: PTypeInfo;
  ArrayTypeData: TArrayTypeData;
begin
  TypInfo := TypeInfo(T);
  if TypInfo.Kind = tkArray then begin
    ArrayTypeData := GetTypeData(TypInfo).ArrayData;
    Writeln(ord(ArrayTypeData.ElType^.Kind));
    Writeln(ArrayTypeData.Size);
    Writeln(ArrayTypeData.ElCount);
    Writeln(ArrayTypeData.DimCount);
  end;
end;

type
  TArr = array [1..32] of char;

var
  Arr: TArr;

begin
  TFoo.Bar<TArr>(Arr);
  Readln;
end.

输出为:

9
64
32
1

注意ord(tkWChar) == 9。因此,这为您提供了执行以下操作的方法:

  1. 检测到类型为数组
  2. 检查它是否具有单一维度。
  3. 检查元素类型是否符合预期。
  4. 检查元素计数是否符合预期。

这就是您检查类型是否满足您的要求所需的全部信息。