我向几位同事展示了这一点,没有人有解释。 我碰巧遇到了这个问题,因为我认为我在代码中发现了一个错误,但是很惊讶地看到代码实际运行了。 这是一个简化版本。 这是用XE-2完成的。
到目前为止我与之交谈的每个人都期望应该抛出NullReferenceException。
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;
答案 0 :(得分:11)
非静态类方法被编译为具有隐藏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
,因此没有理由发生nil指针错误。
更改您的代码以使用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 那样的东西。
答案 1 :(得分:5)
您的方法从不引用Self
指针,因此它恰好起作用。
与C#不同,Delphi不会自动验证Self
(C#中的this
)指针。