Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi中的Fluent接口_Delphi_Fluent Interface - Fatal编程技术网

Delphi中的Fluent接口

Delphi中的Fluent接口,delphi,fluent-interface,Delphi,Fluent Interface,在Delphi中使用的优点和缺点是什么 流畅的接口应该会增加可读性,但我有点怀疑是否有一个包含大量链接方法的长LOC 是否存在任何编译器问题? 是否存在调试问题? 是否存在任何运行时/错误处理问题 例如,在和中使用流畅的界面 更新: 大卫·赫弗南问我关心哪些问题。我对此进行了一些思考,总体问题是“明确指定如何完成”与“让编译器决定如何完成”之间的区别 在AFAICS中,没有关于编译器实际如何处理链式方法的文档,也没有关于编译器应该如何处理链式方法的任何规范 在中,我们可以了解编译器如何向声明为

在Delphi中使用的优点和缺点是什么

流畅的接口应该会增加可读性,但我有点怀疑是否有一个包含大量链接方法的长LOC

是否存在任何编译器问题?
是否存在调试问题?
是否存在任何运行时/错误处理问题

例如,在和中使用流畅的界面


更新:
大卫·赫弗南问我关心哪些问题。我对此进行了一些思考,总体问题是“明确指定如何完成”与“让编译器决定如何完成”之间的区别

在AFAICS中,没有关于编译器实际如何处理链式方法的文档,也没有关于编译器应该如何处理链式方法的任何规范

在中,我们可以了解编译器如何向声明为函数的方法添加两个额外的var参数,以及标准调用约定如何在寄存器中放置三个参数,在堆栈中放置下一个参数。因此,具有2个参数的“fluent函数方法”将使用堆栈,而具有2个参数的“普通过程方法”仅使用寄存器

我们还知道,编译器在优化二进制文件方面发挥了一些神奇的作用(例如),但有时会给程序员带来意想不到的副作用

因此,内存/堆栈/寄存器管理更为复杂,而且编译器可以在无意中产生副作用的情况下发挥一些魔力,这一事实对我来说非常难闻。这就是问题所在


在我阅读了答案(非常好的答案)之后,我的担心大大减少了,但我的偏好仍然是一样的:)

这是一种“一次写入,永不读取”的表示法,如果不查阅所有相关方法的文档,就很难理解。此外,这种表示法与Delphi和C#属性不兼容-如果需要设置属性,则需要回滚到使用
通用符号,因为无法链接属性指定

有编译器问题吗?

没有

是否存在调试问题?

是。由于所有链接的方法调用都被视为一个表达式,即使您像链接的Wikipedia示例那样在多行上编写它们,调试时也会出现问题,因为您无法单步执行它们

是否存在运行时/错误处理问题?

已编辑:我为每个迭代分配了6个属性(实际上每个迭代3次分配相同的2个值)。结论如下:

  • 使用接口:运行时增加70%,具体取决于属性集的数量。只设置两个属性,开销更小
  • 使用对象:使用流畅的界面更快
  • 没有测试记录。它不能很好地处理记录

我个人并不介意那些“流畅的界面”。以前从未听说过这个名字,但我一直在使用它们,特别是在从代码填充列表的代码中。(有点像您发布的XML示例)。我认为它们不难阅读,特别是如果熟悉这种编码并且方法名称有意义的话。至于一行很长的代码,看看维基百科的例子:你不需要把它全部放在一行代码上


我清楚地记得,将它们与
turbopascal
一起用于初始化屏幕,这可能就是我不介意它们的原因,有时也会使用它们。不幸的是,Google让我失望了,我找不到旧TP代码的任何代码示例。

编译器问题:

如果您使用的是接口(而不是对象),则链中的每个调用都会导致引用计数开销,即使始终返回完全相同的接口,编译器也无法知道。因此,您将生成一个更大的代码,具有更复杂的堆栈

调试问题:

调用链被视为一条指令,不能在中间步骤上单步执行或断点。您也不能在中间步骤计算状态。 调试中间步骤的唯一方法是在asm视图中进行跟踪。 如果相同的方法在fluent链中多次出现,调试器中的调用堆栈也将不清晰

运行时问题:

当为链(而不是对象)使用接口时,您必须支付引用计数开销,以及更复杂的异常帧。 你不能在一个链中进行try..finally构造,因此不能保证关闭在流畅的链f.i.中打开的内容

调试/错误日志记录问题:

异常及其堆栈跟踪将链视为一条指令,因此,如果您在.DoSomething中崩溃,并且调用链有几个.DoSomething调用,您将不知道是哪个导致了问题

代码格式问题:


AFAICT现有的任何代码格式化程序都不能正确地布局流畅的调用链,因此只有手动格式化才能使流畅的调用链保持可读性。如果运行自动格式化程序,它通常会将一个链变成可读性混乱。

我会质疑使用“流畅接口”的好处

在我看来,这一点是为了让您避免声明变量。因此,“恐惧与陈述”带来的好处是一样的,但问题却不同(见其他答案)

老实说,我从未理解使用With语句的动机,也不理解使用流畅界面的动机。我的意思是定义一个变量有那么难吗?
所有这些胡言乱语只是为了允许懒惰

我认为,与其增加可读性(乍一看,它似乎是通过减少键入/读取来实现的),不如说它实际上混淆了它

所以,我再次问你,为什么你首先要使用流畅的界面

它是马丁·福勒创造的,所以一定很酷吧?Na
fdsUnreportedMessages.Add(CreateFluentXml
  .UTF8
  .AddChild('LogEntry')
    .AddChild('Time', Now)
    .AddSibling('Severity', msg.MsgID)
    .AddSibling('Message', msg.MsgData.AsString)
  .AsString);
var
  xmlData: IXMLNode;
  xmlDoc : IXMLDocument;
  xmlKey : IXMLNode;
  xmlRoot: IXMLNode;

  xmlDoc := CreateXMLDoc;
  xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction('xml', 
    'version="1.0" encoding="UTF-8"'));
  xmlRoot := xmlDoc.CreateElement('LogEntry');
  xmlDoc.AppendChild(xmlRoot);
  xmlKey := xmlDoc.CreateElement('Time');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(FormatDateTime(
    'yyyy-mm-dd"T"hh":"mm":"ss.zzz', Now));
  xmlKey.AppendChild(xmlData);
  xmlKey := xmlDoc.CreateElement('Severity');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(IntToStr(msg.MsgID));
  xmlKey.AppendChild(xmlData);
  xmlKey := xmlDoc.CreateElement('Message');
  xmlDoc.AppendChild(xmlKey);
  xmlData := xmlDoc.CreateTextNode(msg.MsgData.AsString);
  xmlKey.AppendChild(xmlData);
  fdsUnreportedMessages.Add(xmlKey.XML);
var
  xmlDoc: IXMLDocument;
  xmlLog: IXMLNode;

  xmlDoc := CreateXMLDoc;
  xmlDoc.AppendChild(xmlDoc.CreateProcessingInstruction(
    'xml', 'version="1.0" encoding="UTF-8"'));
  xmlLog := EnsureNode(xmlDoc, 'LogEntry');
  SetNodeTextDateTime(xmlLog, 'Time', Now);
  SetNodeTextInt(xmlLog, 'Severity', msg.MsgID);
  SetNodeText(xmlLog, 'Message', msg.MsgData.AsString);
  fdsUnreportedMessages.Add(XMLSaveToString(xmlDoc));