Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object inheritance involving enumerated types

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

Our product makes extensive use of a third-party component. We don't use the component directly, but instead use a custom descendant of it, to which we've added quite a lot of extra behavior (the custom descendant component was developed several years ago by developers who have since have retired).

In the source unit of the third-party Parent class, some enumerated types are declared, which control various operations of the component:

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

In our descendant class, we want to add new behavior, which would require expanding the selection of enumerated types. Essentially, we want this:

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

Obviously, we want to avoid editing the third-party code. Is it valid to simply redeclare the enumerated type, repeating the original values and adding our new ones, in our own descendent unit? Will it have any effect on existing code?

Edit: Example scenario to (hopefully) clarify. Say you've got a (parent) component for ordering vehicle parts. The parent unit has an enumerated type Tvkind for vehicle kind, with values vkCar and vkCycle defined. These values are used, among other things, to indicate how many wheels the vehicle has, 4 or 2.

Now, in your descendent component, you want to be able to handle 3-wheeled vehicles as well. Extending the Tvkind enumerated type to include a new value vkTrike seems like the obvious approach. But what if you don't have access to or don't want to modify the parent component code?

like image 280
Eric S. Avatar asked Feb 01 '13 14:02

Eric S.


2 Answers

Inheritance for enumeration types doesn't work the same way it works for Classes because code makes assumptions about enumerations that it would never make about a class. For example, given your original enumeration (the TSpecialKind), the third party component likely includes code like this:

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

Even if you could cast something that's not part of that enumeration to the TSpecialKind type, the result of that code above would be undefined (and definitively not good!)

Enumerations might be used in one other way, and if the third party component only uses it that way, then you might be able to do some "wizardry", but I don't recommend it. If the original TSpecialKind is only used through it's TSpecialKinds set type, and then it's only used like this:

if skBeta in VarOfTypeSpecialKinds then
begin
  ...
end;

(continued) then you could introduce a new type that enumerates all of the original values, in the same order, with the same value. If after you do that SizeOf(TSpecialKind) equals SizeOf(TNewType) then you can hard-cast the new set value to the old value and the old code would work the same. But frankly this is hacky, to many conditions for it to work properly, too fragile. The better solution would be to use a new enumeration type that's only used in your descendant component:

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

You'll probably have this set published in a different property; The solution is clean, will mix well with the descendant code and can be used cleanly too. Example:

if (skAlpha in SpecialKind) or (skDelta in ExtraSpecialKind) then
begin
  // Do extra-sepcial mixed stuff here.
end;
like image 153
Cosmin Prund Avatar answered Nov 15 '22 10:11

Cosmin Prund


I don't believe that you can reasonably expect to make the change that you want without modifying the original component.

Let's take your vehicle kind example and delve a bit deeper. I expect that the original component will have code like this:

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

Now, suppose you introduce an enumerated type with an extra enumeration

TExtendedVehicleKind = (vkCar, vkCycle, vkTrike);

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

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

like image 36
David Heffernan Avatar answered Nov 15 '22 09:11

David Heffernan