Delphi 不应该';对设置为nil的对象引用调用Free是否会在每次调用时抛出访问冲突?

Delphi 不应该';对设置为nil的对象引用调用Free是否会在每次调用时抛出访问冲突?,delphi,delphi-xe,Delphi,Delphi Xe,我从单元DBXCommon.pas(在Delphi XE中)获取访问冲突。当我查看代码时,我看到如下内容(在感叹号处): 但是我在DBXCommon.pas中看到了更多类似的构造(首先分配Nil,然后分配Free)。这是我不知道的构造,还是每次调用这段代码时都会导致访问冲突?在调用nil引用时调用Free是安全的,因为它的实现在调用Destroy之前会检查Self nil。请参阅艾伦·鲍尔在《为什么引入TObject.Free中的解释。我在此仅包括相关报价: 在TObject上引入非虚拟自由方法

我从单元
DBXCommon.pas
(在Delphi XE中)获取访问冲突。当我查看代码时,我看到如下内容(在感叹号处):


但是我在
DBXCommon.pas
中看到了更多类似的构造(首先分配Nil,然后分配Free)。这是我不知道的构造,还是每次调用这段代码时都会导致访问冲突?

在调用
nil
引用时调用
Free
是安全的,因为它的实现在调用
Destroy
之前会检查
Self nil
。请参阅艾伦·鲍尔在《为什么引入
TObject.Free
中的解释。我在此仅包括相关报价:

在TObject上引入非虚拟自由方法的唯一原因是在析构函数中用作以下内容的简单缩写:

如果为零,则
毁灭;

TObject.Free
基本上是作为
实现的,如果Self-nil,那么Destroy
,因此上面的代码不应该引发任何异常。

在空引用上调用
Free
总是安全的。查看
TObject.Free
的实现以了解原因

此代码是factory函数的一个示例。它的任务是创建一个类的新实例,但如果失败,它需要确保在抛出异常时不会泄漏半创建的实例,因此它调用
Free
。当它确定会成功时,它将结果的所有权转移给调用者。它仍然调用
Free
,但是如果它已经转移了所有权,那么它最终会在一个空引用上调用
Free
,并且不会造成任何伤害。此代码用于转移所有权:

Result := Connection;
Connection := nil;
我编写工厂函数的方法将消除单独的
连接
变量。我会直接在
result
中构建结果,但如果出现异常,则释放它,如下所示:

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Result := ConnectionBuilder.CreateConnection;
    try
      Result.Open;
    except
      Result.Free;
      raise;
    end;
  finally
    ConnectionBuilder.Free;
  end;
end;

这也有同样的效果。

谢谢,在我的脑海深处,我知道这一点。但是,对于未经培训的人来说,按照您所展示的方式编写会更清晰。+1这是编写返回新对象的函数的惯用方法。我同意David的观点,但如果不重新引发异常,则应将结果设置为零。这是我们在性能关键代码中所做的事情,其中检查非nil结果比引发和处理异常更快…@Marjan,如果您不重新引发异常,那么您必须处理它们。那很好。修复导致引发异常的问题是一件非常好的事情。但大多数代码无法修复所有(甚至任何)可能导致异常的问题。任何捕获异常但既不修复问题也不重新引发它的代码都是错误的。@RobKennedy:这里没有我的参数:)。我所说的代码实际上是为了尽可能地继续下去,尝试其他方法,或者回滚它开始的任何东西。这是一个内存超过80 GB的服务器,加载时间长达几个小时,我们不希望它因为某些东西不可用而崩溃……Dupe-@user这似乎不是一个Dupe。现在还没有吸取教训吗?人们应该停止链接到Embarcadero的网页。他们在那里只呆了2-3年。然后。。。帕夫夫。走了:):)
Result := Connection;
Connection := nil;
function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Result := ConnectionBuilder.CreateConnection;
    try
      Result.Open;
    except
      Result.Free;
      raise;
    end;
  finally
    ConnectionBuilder.Free;
  end;
end;