Delphi 为什么没有引发NullReferenceException?
我已经向几位同事展示了这一点,但没有人对此做出解释。 我偶然发现了这一点,因为我认为我在代码中发现了一个bug,但惊讶地发现代码实际上运行了。 这是一个简化的版本。 这是用XE-2完成的 到目前为止,我与之交谈过的每个人都希望抛出NullReferenceExceptionDelphi 为什么没有引发NullReferenceException?,delphi,delphi-xe2,Delphi,Delphi Xe2,我已经向几位同事展示了这一点,但没有人对此做出解释。 我偶然发现了这一点,因为我认为我在代码中发现了一个bug,但惊讶地发现代码实际上运行了。 这是一个简化的版本。 这是用XE-2完成的 到目前为止,我与之交谈过的每个人都希望抛出NullReferenceException TUnexplainable = class(TObject) public function Returns19: Integer; end; function TUnexplainable.Retur
TUnexplainable = class(TObject)
public
function Returns19: Integer;
end;
function TUnexplainable.Returns19: Integer;
begin
Result := 19;
end;
下面的测试永远不会工作,但它会成功运行。为什么没有引发NullReferenceException
procedure TTestCNCStep.ShouldNeverEverWorkV4;
var
Impossible: TUnexplainable;
Int1: Integer;
begin
Impossible := nil;
Int1 := Impossible.Returns19; // A Null Reference Exception should ocurr here!!! Instead the method Returns19 is actually invoked!!!!
Check(Int1 = 19);
end;
您的方法从不引用
Self
指针,因此它正好工作
与C#不同,Delphi不会自动验证C#中的
Self
(此)指针。非静态类方法编译为带有隐藏Self
参数的独立函数。因此,从编译器的角度来看,您的代码基本上执行以下操作:
//function TUnexplainable.Returns19: Integer;
function TUnexplainable_Returns19(Self: TUnexplainable): Integer;
begin
Result := 19;
end;
//procedure TTestCNCStep.ShouldNeverEverWorkV4;
procedure TTestCNCStep_ShouldNeverEverWorkV4(Self: TTestCNCStep);
var
Impossible: TUnexplainable;
Int1: Integer;
begin
Impossible := nil;
Int1 := TUnexplainable_Returns19(Impossible);
Check(Int1 = 19);
end;
正如您所看到的,Returns19()
没有引用任何东西的Self
,因此没有理由发生零指针错误
更改代码以使用Self
,然后您将看到预期的错误:
type
TUnexplainable = class(TObject)
public
Number: Integer;
function ReturnsNumber: Integer;
end;
function TUnexplainable.ReturnsNumber: Integer;
begin
Result := Number;
end;
procedure TTestCNCStep.ShouldNeverEverWorkV4;
var
Impossible: TUnexplainable;
Int1: Integer;
begin
Impossible := nil;
Int1 := Impossible.ReturnsNumber; // An EAccessViolation exception will now occur here!!!
end;
我已经向几位同事展示了这一点,但没有人对此做出解释
我会非常担心与一群同事(假设他们是程序员)一起工作,他们不了解类方法和Self
参数的实际工作原理,也不了解通过nil指针调用类方法如何避免空指针错误。这类似于面向对象编程101之类的东西。代码不引用堆上的任何对象字段,因此无需使用自引用。另请参阅,谢谢我现在理解了。来自C++和C i,我确信这会导致异常。我在使用Delphi时遇到的一个困难是,我没有发现涉及该语言的最新书籍。所以,即使你想深入了解德尔菲,几乎没有任何新的资料可用。我所描述的是C++如何工作。然而,C#是不同的,因为.NET是一个托管环境,它检查对象指针和方法调用的有效性。@santiagoIT-这里涉及的行为对于Delphi来说并不新鲜。它是Delphi自1.0版以来生成的运行时代码行为的基本部分。e、 TObject上的“Free”方法显式地依赖于使用NIL对象引用调用方法的能力。简单地说,优秀的Delphi对于这类事情仍然是高度相关和有用的。另外值得注意的是,这种行为仅限于非虚拟方法。使用声明为虚拟的Returns19尝试完全相同的操作,并使用NIL对象引用调用该操作将导致异常。@Deltics:“此行为仅限于非虚拟方法”-非常正确。调用virtual
或dynamic
方法需要访问对象的vtable/dispatch表,该表只能通过有效的对象指针访问。