为什么没有引发 NullReferenceException?
Why is no NullReferenceException raised?
我已经把这个给几个同事看了,没有人有解释。
我 运行 纯属偶然,因为我认为我在我们的代码中发现了一个错误,但惊讶地发现代码实际上是 运行。
这是一个简化版本。
这是用 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;
您的方法从不引用 Self
指针,因此它恰好有效。
与 C# 不同,Delphi 不会自动验证 Self
(C# 中的 this
)指针。
非静态 class 方法被编译成一个带有隐藏 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;
I have shown this to several colleagues and no one has an explanation.
我会非常担心与一群不了解 class 方法和 Self
参数实际工作原理以及调用方法的基础知识的同事(假设他们是程序员)一起工作通过 nil 指针的 class 方法可以避免空指针错误。这就像 面向对象编程 101 之类的东西。
我已经把这个给几个同事看了,没有人有解释。 我 运行 纯属偶然,因为我认为我在我们的代码中发现了一个错误,但惊讶地发现代码实际上是 运行。 这是一个简化版本。 这是用 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;
您的方法从不引用 Self
指针,因此它恰好有效。
与 C# 不同,Delphi 不会自动验证 Self
(C# 中的 this
)指针。
非静态 class 方法被编译成一个带有隐藏 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;
I have shown this to several colleagues and no one has an explanation.
我会非常担心与一群不了解 class 方法和 Self
参数实际工作原理以及调用方法的基础知识的同事(假设他们是程序员)一起工作通过 nil 指针的 class 方法可以避免空指针错误。这就像 面向对象编程 101 之类的东西。