Inheritance of objects using the listed types

Delphi 2007, moving to Delphi XE over the next year.

Our product is widely used third-party component. We do not use the component directly, but instead use its own descendant, to which we added quite a lot of additional behavior (the custom descendant component was developed several years ago by developers who have since resigned).

In the source block of the third-party Parent class, some of the listed types are declared that control the various operations of the component:

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

In our descendant class, we want to add a new behavior that will require an expanded selection of enumerated types. Essentially, we want this:

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

Obviously, we want to avoid editing third-party code. Does it make sense to simply redefine the numbered type by repeating the original values โ€‹โ€‹and adding our new ones to our own child unit? Will this have any effect on existing code?

Edit: Example script (hopefully) clarified. Say you have a (parent) component for ordering car parts. The parent unit is of type Tvkind with an enumeration for the vehicle type with the values โ€‹โ€‹vkCar and vkCycle. These values โ€‹โ€‹are used, among other things, to indicate the number of wheels of a vehicle, 4 or 2.

Now, in your ceiling component, you want to be able to handle 3-wheeled vehicles. Extending the enumerated type Tvkind to include the new vkTrike value seems obvious. But what if you do not have access or do not want to change the code of the parent component?

+7
source share
3 answers

I do not believe that you can reasonably expect to make the changes you want without changing the source component.

Take an example of your car and go deeper. I expect the source component to have code like this:

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

Now suppose you enter a numbered type with an extra enumeration

 TExtendedVehicleKind = (vkCar, vkCycle, vkTrike); 

If the case statement above, with ExtendedKind equal to vkTrike , the method will not be called.

Now, perhaps the behavior you want from the original control can be achieved by setting Kind to vkCar or vkCycle when ExtendedKind is vkTrike . But this seems unlikely to me. Only you can know for sure, because only you have the code and know what your actual problem is.

+2
source

Inheritance of enumeration types does not work the same as for classes, because the code makes assumptions about enums, which it will never do for the class. For example, given your initial enumeration ( TSpecialKind ), a third-party component probably includes the following code:

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

Even if you can use something that is not part of this enumeration for the TSpecialKind type, the result of the above code will be undefined (and, of course, not very good!)

Enumerations can be used in a different way, and if a third-party component uses it only this way, then you can do some kind of โ€œmagicโ€, but I do not recommend it. If the original TSpecialKind used only through the TSpecialKinds type, then it is used only like this:

 if skBeta in VarOfTypeSpecialKinds then begin ... end; 

(continued), then you can introduce a new type that lists all the original values โ€‹โ€‹in the same order, with the same value. If after that SizeOf(TSpecialKind) is equal to SizeOf(TNewType) , you can apply the new set value to the old value, and the old code will work the same. But frankly, this is a hack, in many conditions, for it to work correctly, too fragile. A better solution would be to use a new type of enumeration that was used only in your ceiling component:

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

This set is likely to be published in another property; The solution is clean, goes well with the descendant code, and can also be used cleanly. Example:

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

It was pointed out that "it is necessary to extend the listed property type."

Quick first sentence. Add your listing as a new property wrapper to an existing property:


Potential parent class code:


 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; 

Descendant class code:


 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; 
  • Extra.

If possible, always add the value "null" or "dummy" to your own enumerations, usually the first value:

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

Greetings.

-2
source

All Articles