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 我是否应该将接口作为const传递?_Delphi_Interface_Constants - Fatal编程技术网

Delphi 我是否应该将接口作为const传递?

Delphi 我是否应该将接口作为const传递?,delphi,interface,constants,Delphi,Interface,Constants,我最近(又)遇到了一份推荐信 如果您的方法被声明为将接口变量作为const传递,则会发生这种情况,例如: procedure Frob(const Grob: IGrobber); 修复方法是简单地删除常量: procedure Frob(Grob: IGrobber); 我知道const(和var,和out)允许您通过引用传递项目。对于结构,这将保存一个参数副本;相反,您只需将指针传递给该项 在对象/指针/接口的情况下,不需要通过引用传递,因为它是一个引用;它已经可以放在登记册里了 为了不

我最近(又)遇到了一份推荐信

如果您的方法被声明为将接口变量作为
const
传递,则会发生这种情况,例如:

procedure Frob(const Grob: IGrobber);
修复方法是简单地删除
常量

procedure Frob(Grob: IGrobber);
我知道
const
(和
var
,和
out
)允许您通过引用传递项目。对于结构,这将保存一个参数副本;相反,您只需将指针传递给该项

对象
/
指针
/
接口
的情况下,不需要通过引用传递,因为它是一个引用;它已经可以放在登记册里了

为了不再有这个问题,我参加了一次十字军东征。我搜索了所有源树以查找:

const [A-Za-z]+\: I[A-Z]
我删除了大约150个实例,其中我将一个接口作为const传递

但有些是我无法改变的。
TWebBrowser
回调事件声明为:

OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
                                    \___/
                                      |
                                      ?
我走得太远了吗?我做过坏事吗

编辑:或者,用一种不太“基于观点”的方式来表达:不将接口作为常量传递有什么严重的缺点吗

奖金:当Delphi不(总是)增加接口引用计数时,他们违反了:

引用计数规则 规则1:必须为接口指针的每个新副本调用AddRef,为接口指针的每个销毁调用Release,除非后续规则明确允许

规则2:一段代码中关于接口指针的两个或多个副本的生命周期的开始和结束关系的专门知识可以允许省略AddRef/Release

因此,虽然这可能是编译器可以利用的优化,但它必须正确地执行,以避免违反规则

如果您的方法被声明为以常量形式传递接口变量,则会发生这种情况,例如:

procedure Frob(const Grob: IGrobber);
那不太对。为了防止泄漏,代码中必须没有引用新创建对象的内容。所以如果你写:

Frob(grob);
Frob(TGrobberImplementer.Create);
没有问题,因为接口
grob
已经至少有一个引用

当你写下以下内容时,问题就出现了:

Frob(grob);
Frob(TGrobberImplementer.Create);
在这种情况下,没有任何内容引用接口,因此它被泄漏。好吧,只要
Frob
的实现中没有引用它,它就会被泄露

我做过坏事吗

那要看情况了。我认为你所做的不会带来任何特别糟糕的事情。性能方面有一个缺点,因为所有接受接口参数的函数现在都必须使用隐式try/finally块添加和释放引用。只有你才能判断这是否重要

更重要的问题与您无法控制的代码有关。你给

procedure OnDocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant);
举个例子。这没有问题,因为您从未调用该方法。它是您实现的事件处理程序。框架调用它,并传递一个已经被引用的接口

真正的问题来自RTL中声明的方法或您调用的任何其他第三方代码。如果您正在调用这些方法,并且它们使用
const
接口参数,那么您可能会落入陷阱

工作很容易,尽管很累

grob := TGrobberImplementer.Create;
Frob(grob);
我处理这个问题的理由如下:

  • 按值传递接口参数会带来性能代价
  • 我不能确保我调用的每个方法都会按值接受接口参数
  • 因此,我接受这样一个事实:至少在某些时候我需要处理调用
    const
    接口参数
  • 由于我不得不处理一些时间,因为我讨厌不一致,我选择接受处理它的所有时间
  • 因此,我选择将我编写的方法中的所有接口参数设置为
    const
  • 因此,我确保我永远不会将接口作为参数传递,除非它已经被变量引用

  • Embarcadero已经知道这件事很多年了。丹尼·索普和巴里·凯利都告诉我这是个缺陷。最近,Marco在Google+上发布了一些漏洞,解释了为什么改变并不简单。我猜他们在编译器中有一个实现选择,如果不进行重大的重新设计就很难解决这个问题,而且他们真的不知道如何进行重新设计。我相信你知道这一点,但使用接口/string/dyn\u数组的
    const
    的要点并不是要避免
    按值传递
    ,但是为了避免引用计数和隐藏,最后尝试使用它。问题中的
    pass\u by\u value
    部分把问题搞糊涂了。@Johan,这是一个有趣的观点。语义上
    const
    表示函数的实现不能修改参数。参考计数等是在知道未进行任何修改的基础上进行的优化。如果编译器更有意义的话,它甚至可以通过观察没有进行任何修改来对值参数进行同样的优化。@IanBoyd:您不需要从接口参数中大量删除每一个
    const
    。您真正需要做的就是在代码中搜索任何情况,其中
    Create()
    ”是在函数调用中内联的接口对象,例如:
    Func(TMyObject.Create)
    。这是唯一的漏洞,而不是
    常量本身。@IanBoyd:Delphi没有违反COM规则。仅将接口指针作为输入的函数不需要接触接口的引用计数。如果伊芙