Free Pascal Hack 之访问对象的 protected 成员
默认情况下,Free Pascal 类中 protected 成员只能被当前类、子类以及同一个 Unit 中的代码访问。然而在实践中,往往会碰到访问其他 Unit 中定义的类实例对象的 protected 成员的需求。通常我们会通过类继承以及修改原始类成员的代码来暴露 protected 成员,然而当该对象来自第三方库,甚至 FCL 和 LCL 时,情况就会比较棘手。有一种 Hack 方法可以实现这个需求,且不用修改原始 Unit 的代码,没有额外副作用。
假设在 unit1.pas 中定义了类 TFoo
:
pascalunit Unit1;
interface
type
TFoo = class
protected
FMember: string;
end;
var
Foobar: TFoo;
implementation
type
TBar = class(TFoo);
initialization
Foobar := TBar.Create;
finalization
FreeAndNil(Foobar);
end.
在 unit2.pas 中访问 Foo.FMember
成员:
pascalunit Unit2;
interface
uses
Unit1;
implementation
type
TFooAccessProtected = class(TFoo);
function GetFooProtectedMember: string;
begin
Result := TFooAccessProtected(Foobar).FMember;
end;
end.
虽然 TFooAccessProtected
和 TBar
两个类型并不在同一条继承链上,但是 TFooAccessProtected
和 TFoo
对象的内存布局是一致的。而在 Unit2 中的代码是可以访问 TFooAccessProtected
及其父类中所有的 protected 成员的。通过强制类型转换,使得 TFoo
的 FMember
暴露在 Unit2 中。
不过需要注意:在 Debug 模式下,Hack 代码 TFooAccessProtected(Foobar)
运行时会抛出 RunError(219)
以及 EInvalidCast
异常。这是因为 Debug 模式开启了编译指令“验证方法调用 (-CR)”。可以在代码里临时禁用该编译器指令:
pascalfunction GetFooProtectedMember: string;
begin
{$IFDEF DEBUG}{$objectChecks-}{$ENDIF}
Result := TFooAccessProtected(Foobar).FMember;
{$IFDEF DEBUG}{$objectChecks+}{$ENDIF}
end;