涉及枚举类型的对象继承

德尔福2007年,明年转向Delphi XE。

我们的产品广泛使用第三方组件。 我们不直接使用这个组件,而是使用它的一个自定义的后代,我们在其中添加了很多额外的行为(自定义后代组件是几年前由已经退役的开发人员开发的)。

在第三方Parent类的源单元中,声明了一些枚举类型,它们控制组件的各种操作:

TSpecialKind = (skAlpha, skBeta, skGamma);
TSpecialKinds = set of TSpecialKind;

在我们的后代类中,我们想要添加新的行为,这需要扩展枚举类型的选择。 基本上,我们想要这样做:

TSpecialKind = (skAlpha, skBeta, skGamma, skDelta, skEpsilon);
TSpecialKinds = set of TSpecialKind;

显然,我们希望避免编辑第三方代码。 在我们自己的后代单元中重新声明枚举类型,重复原始值并添加新的类型是否有效? 它会对现有的代码有什么影响吗?

编辑:示例场景(希望)澄清。 假设你有一个用于订购车辆零件的(父母)组件。 父单元具有用于车辆类型的枚举类型Tvkind,定义了值vkCar和vkCycle。 除此之外,这些值用于指示车辆有多少个车轮,4或2个。

现在,在你的后代组件中,你也希望能够处理三轮车。 将Tvkind枚举类型扩展为包含新值vkTrike看起来很明显。 但是,如果您无权访问或不想修改父组件代码呢?


我不相信您可以合理地期望在不修改原始组件的情况下进行所需的更改。

让我们以您的车辆为例,深入研究一下。 我期望原始组件具有这样的代码:

case Kind of
vkCar:
  CarMethod;
vkCycle:
  CycleMethod;
end;

现在,假设你引入了一个带枚举的枚举类型

TExtendedVehicleKind = (vkCar, vkCycle, vkTrike);

如果上面的case语句运行,并且ExtendedKind等于vkTrike ,则不会调用任何方法。

现在,当ExtendedKindvkTrike时,可以通过将Kind设置为vkCarvkCycle来实现从原始控件中获得的vkTrike 。 但对我来说这似乎不太可能。 只有你能确定地知道,因为只有你有代码,并且知道你的实际问题是什么。


枚举类型的继承与Classes的工作方式不同,因为代码对枚举进行了假设,而这种枚举永远不会对类进行。 例如,给定您的原始枚举( TSpecialKind ),第三方组件可能包含如下代码:

var Something: TSpecialKind;
[...]
case Something of
  skAlpha: ;
  skBeta: ;
  skGamma: ;
end;

即使您可以将某些不属于该枚举的部分转换为TSpecialKind类型,上面的代码的结果也是不确定的(并且肯定不是很好!)。

枚举可以用另一种方式使用,如果第三方组件只用这种方式,那么你可能会做一些“巫术”,但我不推荐它。 如果原始TSpecialKind只能通过它的TSpecialKinds设置类型使用,那么它仅用于这样的情况:

if skBeta in VarOfTypeSpecialKinds then
begin
  ...
end;

(续),那么你可以引入一个新的类型,它以相同的顺序枚举所有的原始值,并具有相同的值。 如果在你做这个SizeOf(TSpecialKind)等于SizeOf(TNewType)之后,你可以将新的设定值强制转换为旧的值,旧的代码也可以工作。 但坦率地说,这是很冒险的,在许多条件下它能正常工作,太脆弱了。 更好的解决方案是使用仅在您的后代组件中使用的新枚举类型:

type TExtraSpecialKind = (skDelta, skEpsilon);
     TExtraSpecialKinds = set of TExtraSpecialKind;

你可能会在不同的属性中发布这个集合; 解决方案很干净,可以很好地与后代代码混合使用,也可以干净地使用。 例:

if (skAlpha in SpecialKind) or (skDelta in ExtraSpecialKind) then
begin
  // Do extra-sepcial mixed stuff here.
end;

在“需要扩展属性的枚举类型”中。

快速的第一条建议。 将您的枚举添加为现有属性的新属性包装:


潜在的父类代码:


unit AcmeMachines;

interface

type
   FoodCanEnum =
   (
     None,
     Fish,
     Bird,
     Beef
   );

   AcmeCanAutoOpenMachineClass= class (object)
   protected
   { protected declarations }

      F_FoodCanEnum: FoodCanEnum;

      function getFoodEnumProperty: FoodCanEnum;
      procedure setFoodEnumProperty(const AValue: FoodCanEnum);
   public
   { public declarations }

      property FoodEnumProperty
        read getFoodEnumProperty write setFoodEnumProperty;
   end;

implementation

   function AcmeCanAutoOpenMachineClass.getMyFoodEnumProperty: FoodCanEnum;
   begin        
     Result := F_FoodCanEnum;
   end;

   procedure AcmeCanAutoOpenMachineClass.setMyFoodEnumProperty
     (const AValue: CatFoodCanEnum);
   begin    
     FoodEnumProperty:= AValue;

     // do some specific business logic
   end;

end;

后代类代码:


unit UmbrellaMachines;

interface
uses AcmeMachines;

type
   CatFoodCanEnum =
   (
     None, <--- matches "AcmeMachines.None"
     Fish, <--- matches "AcmeMachines.Fish"
     Bird, <--- matches "AcmeMachines.Bird"
     Beef, <--- matches "AcmeMachines.Beef"
     Tuna,
     Chicken
   );

   UmbrellaCanAutoOpenMachineClass = class (AcmeCanAutoOpenMachineClass)
   protected
   { protected declarations }

      F_CatFoodCanEnum: CatFoodCanEnum;

      function getMyFoodEnumProperty: CatFoodCanEnum;
      procedure setMyFoodEnumProperty(const AValue: CatFoodCanEnum);
   public
   { public declarations }

      // new property, "wraps" existing property
      property MyFoodEnumProperty
        read getMyFoodEnumProperty write setMyFoodEnumProperty;
   end;

implementation

   function UmbrellaCanAutoOpenMachineClass.getMyFoodEnumProperty: CatFoodCanEnum;
   begin
     // wrap existing "FoodEnumProperty" property, using an existing value as dummy

     Result := F_CatFoodCanEnum;
   end;

   procedure UmbrellaCanAutoOpenMachineClass.setMyFoodEnumProperty
     (const AValue: CatFoodCanEnum);
   begin
     // wrap existing property, using an existing value as dummy
     // may be another value if necessary
     AcmeCanAutoOpenMachineClass.ExistingFoodEnumProperty := AcmeMachines.None;

     F_CatFoodCanEnum := AValue;
     // add extended business logic for this class instances
   end;

end;
  • 额外。
  • 如果可能,请始终为您自己的枚举添加“null”或“dummy”值,通常为第一个值:

     type
       CatFoodCanEnum =
       (
         None, // <--- these one
         Tuna,
         Chicken,
         Beef
       );
    

    干杯。

    链接地址: http://www.djcxy.com/p/69135.html

    上一篇: Object inheritance involving enumerated types

    下一篇: How can I preserve the stack trace when exception is thrown on non