【问题标题】:Changing type of class field in inherited class更改继承类中的类字段类型
【发布时间】:2019-12-16 16:23:31
【问题描述】:

我使用的是最新的 Delphi 10.3.3

我有几个主类,它们是从同一个父类扩展而来的,还有一些,我们称之为反射类,它们也有相同的父类。我希望主类的实例能够链接到相应的反射实例,但我遇到了这个问题(我将其简化为这个例子):

主要课程:

TMainClass = class
  Link: TReflectionClass;
end;
TCarMainClass = class(TMainClass)
  Link: TCarReflectionClass;
end;

反射类:

TReflectionClass = class;
TCarReflectionClass = class(TReflectionClass);

问题在于Link字段。我希望将 TCarMainClass 中的 Link 字段直接定义为 TCarReflectionClass 以避免对大量代码进行类型转换(并且还会冒一些错误的风险),但是如果我这样定义类, TCarMainClass.Link 只是隐藏 TMainClass.Link - 它是定义为具有相同名称的不同字段。这不好,因为它消耗额外的内存,主要是我无法从父类访问我想要的那个字段(作为指向通用实例的指针)。

当然,我可以通过将字段设为公共类型私有并定义属性 setter/getter 来处理每个类中的重新键入来解决这个问题。但这对于这些类来说是很多额外的代码,而且由于调用 getter/setter 方法,每个 get/set 也会产生开销。

我的问题是 - 我是否错过了一些简单的技巧,告诉编译器我希望子类中的字段与父类中的某些字段占用相同的内存位置?

谢谢

【问题讨论】:

  • 不确定 Delphi 的具体情况,但通常您尝试做的不是类型安全的,因为该字段是可变的。如果有人持有对基类实例的引用,并且编译时类型是针对基类的,那么他们可以使用基类的属性类型来设置其属性值。该更改将通过其他引用可见,包括其编译时类型承诺相同属性将具有比刚设置的更严格类型的引用。因此,首先不应允许更改;您应该只对不可变字段执行此操作。
  • 各种解决方案浮现在脑海中,包括泛型和接口,但两者都不会让这变得更轻量级,如果它们适用完全取决于你将如何使用它。你的问题在这方面有点含糊。不过,我对您的性能要求很好奇,如果您想在这里避免添加 getter 和 setter(一种 OOP 的良好做法)。
  • @kaya3 实际上这种情况是类型安全的,因为 TCarReflectionClass 也是从 TReflectionClass 继承的,TReflectionClass 是另一个父类的类型。然而这是真的(除非 Delphi 对这种情况进行特殊检查)它也允许不安全的强制转换。但是 Delphi 不太关心类型安全,它不像 C# 那样管理,所以它允许手动将任何类重新键入到任何类,在最坏的情况下会导致运行时错误。所以我的问题更多是关于如何告诉编译器做什么(如果可能的话),而不是这是否可能 - 因为手动转换现在对我来说很好,这很烦人。
  • @GolezTrol 在处理过程中,我经常访问这些链接,例如每秒可能数千万次。当我编写像 TCarReflectionClass(Obj.Link).DoSomething 这样的代码时,它基本上是零成本转换 - 它只是告诉编译器我知道我在做什么并且不发出警告,但是这种转换不会输出任何代码。当我使用 getter 时,它需要调用过程,该过程只会执行此虚拟重新输入,并且基本上只需 Result := A; 即可转换为函数。但是,这远非零成本,它会稍微影响性能。
  • 基本上我只是想避免在代码中一直编写 TCarReflectionClass(Obj.Link) 。我想让编译器知道,如果 Obj 是 TCarMainClass,那么 Link 不是 TReflectionClass 而是 TCarReflectionClass。我在代码和不同的类上使用它真的很多,它确实降低了代码的可读性,有很多这样的演员。

标签: oop delphi pascal


【解决方案1】:

主要是我无法从父类访问该字段

虽然父类确实无法访问后代的字段,但没有什么可以阻止您同步 2。

procedure TCarMainClass.SetLink(const Value : TCarReflectionClass);
begin
  FLink := Value;
  TMainClass(Self).FLink := Value;
end;

现在,如果您绝对不需要额外的内存使用和设置器,那么唯一剩下的选项(我目前能想到的)就是 GolezTrol 建议的泛型。

TMainClass<T : TReflectionClass> = class
  Link: T;
end;

TCarMainClass = class(TMainClass<TCarReflectionClass>)
end;

但这可能会破坏您的设计,因为 TCarMainClass 将与 TMainClass&lt;TReflectionClass&gt; 不兼容。 (如果您想了解原因,请搜索术语协方差/逆变)。

【讨论】:

    猜你喜欢
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-25
    • 2013-12-04
    • 1970-01-01
    • 2010-11-08
    • 1970-01-01
    相关资源
    最近更新 更多