【问题标题】:How to create a type that is string?如何创建一个字符串类型?
【发布时间】:2013-01-24 18:08:52
【问题描述】:

blog entry from Raymond Chen today 让我意识到我遇到的问题的优雅解决方案。

各种shell函数,而不是全部采用ITEM­ID­LIST结构,可以只接受:

  • ITEM­ID_CHILD
  • ID­LIST_RELATIVE
  • ID­LIST_ABSOLUTE
  • ITEM­ID_CHILD_ARRAY

结构。这些结构都相同,但现在您可以在编译器级别强制执行概念类型。

返回德尔福

我有一组函数:

  • 一些只有走一条路:(例如C:\Users\Ian\Desktop\AllisonAngel.jpg
  • 一些采用文件名:(例如AllisonAngel.jpg
  • 有些拿文件夹:(例如C:\Users\Ian\Desktop

现在它们都被声明为string,例如:

 function GetFilenames(Folder: string; ...): ...
 function IsValidBatchFilename(Path: string): ...
 function GetReportType(Filename: string): ...

我必须相信我传递的是正确的东西;我相信开发人员(例如 me)知道以下之间的区别:

  • 路径
  • 文件名
  • 和一个文件夹

我想更改函数以使用 "typed" 字符串:

 function GetFilenames(Folder: TFolderOnly; ...): ...
 function IsValidBatchFilename(Path: TFullPath): ...
 function GetReportType(Filename: TFilenameOnly): ...

地点:

type
   TFullPath = type string;
   TFolderOnly = type string;
   TFilenameOnly = type string;

除非没有实际的打字发生:

var
   dropFolder: string;
begin
   dropFolder := GetDropFolderPath(LCT);

   GetFilenames(dropFolder); <-- no compile error

end;

我想要的是一个 "distinct" 类型的字符串;即string,只要它是长度前缀、引用计数、空终止。

【问题讨论】:

  • 这就是 Delphi 比较的地方,例如到 C# 是弱的。据我所知,这是不可能的。

标签: delphi types delphi-5


【解决方案1】:

您可以使用高级记录来完成此操作。例如,你可以这样做

type
  TFileName = record
    FFileName: string;
  public
    class function IsValidFileName(const S: string): boolean; static;
    class operator Implicit(const S: string): TFileName;
    class operator Implicit(const S: TFileName): string;
  end;

implementation

class function TFileName.IsValidFileName(const S: string): boolean;
begin
  result := true {TODO};
end;

class operator TFileName.Implicit(const S: string): TFileName;
begin
  if IsValidFileName(S) then
    result.FFileName := S
  else
    raise Exception.CreateFmt('Invalid file name: "%s"', [S]);
end;

class operator TFileName.Implicit(const S: TFileName): string;
begin
  result := S.FFileName;
end;

TPathTFolder 也是如此。优点:

  • 期待TPath 的函数将不接受TFileName(或其他组合)。
  • 您仍然可以将TPath 分配给/来自常规字符串。如果您从字符串转换为 TPath,您将自动检查字符串以查看其是否包含有效路径。
  • (可选)您可以通过编写更多 Implicit 类运算符来指定如何将 TPath 分配给 TFileName(或其他组合)。

【讨论】:

  • 虽然我认为在 Delphi 5 中不可能,但这是个好主意! [+1]
  • @TLama 我什么也不想说;并接受最受欢迎的答案。
【解决方案2】:

为每种字符串类型创建不同的记录类型。不同的记录类型不兼容分配,即使字符串类型是兼容的。

type
  TFullPath = record value: string end;
  TFolderOnly = record value: string end;

Chen 的文章将新的 shell 功能与创建不同句柄类型的经典 STRICT 宏进行了比较,我记得,声明不同的结构正是 STRICT 宏的工作原理。

【讨论】:

  • 如果我复制TFullPaths 的实例,引用计数是否会保留? (例如FullPath2 := FullPath1;
  • 是的,记录中的字符串与其他任何地方的字符串分配相同,@Afrazier。
【解决方案3】:

Delphi 处理基本类型的方式,我觉得这是一个“你不能从这里到达那里”的例子。

您的字符串类型声明都将满足 Delphi 的类型兼容性和赋值兼容性规则。他们将限制的是带有var 参数的程序声明。如果您将函数声明更改为 var 参数而不是引用或值参数,您将在最后一个示例中收到编译错误。

说了这么多,反正都没用。即使使用TFilenameOnly 类型,您仍然无法确保输入只是一个文件名,并且无论如何都必须在您的过程中测试内容。

【讨论】:

  • 如果是caller wants to use a big enough hammer to smash a square peg in a round hole,那不是问题。但它确实解决了将 red 圆钉插入 green 圆钉的孔中的问题。
  • 它不会为您的色盲用户解决任何问题。 :-P 简单类型的别名的问题是您没有任何方法来确保内容正确。它在错误的抽象级别上攻击问题。如果您切换到可以在分配时在代码中验证值的课程或高级记录,那么您就会有所收获。充其量,Delphi 中的类型别名只不过是对其他程序员的语义提示。
  • 这就是为什么我不想要一个别名,而是一个独特的type(这就是为什么我没有将它声明为TFolder = string , 但是TFolder = type string)
  • 问题是这些类型仍然高度兼容。 Delphi's Help 明确指出:[...]if your purpose in defining a new type is to utilize runtime type information, for example, to associate a property editor with properties of a particular type - the distinction between 'different name' and 'different type' becomes important.) 在声明的背后,它们仍然只是字符串。
  • 虽然我无法回答为什么会出现这种情况,但这种不一致已记录在案:Type Compatibility and Identity 大多数情况都围绕简单的类型、字符串和变体。
猜你喜欢
  • 1970-01-01
  • 2021-09-28
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
  • 2019-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多