7

Delphi 2007、翌年に Delphi XE に移行。

当社の製品は、サードパーティのコンポーネントを幅広く使用しています。コンポーネントを直接使用するのではなく、そのカスタムの子孫を使用します。これには、かなり多くの追加の動作が追加されています (カスタムの子孫コンポーネントは、退職した開発者によって数年前に開発されました)。

サードパーティの Parent クラスのソース ユニットでは、コンポーネントのさまざまな操作を制御するいくつかの列挙型が宣言されています。

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

子孫クラスでは、列挙型の選択を拡張する必要がある新しい動作を追加したいと考えています。基本的に、これが必要です:

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

明らかに、サードパーティのコードを編集することは避けたいと考えています。独自の子孫ユニットで、元の値を繰り返し、新しい値を追加して、列挙型を単純に再宣言することは有効ですか? 既存のコードに影響はありますか?

編集:(うまくいけば)明確にするシナリオの例。車の部品を注文するための (親) コンポーネントがあるとします。親ユニットには、値 vkCar および vkCycle が定義された、車両の種類の列挙型 Tvkind があります。これらの値は、とりわけ、車両の車輪数 (4 または 2) を示すために使用されます。

ここで、子孫コンポーネントで、三輪車も処理できるようにしたいと考えています。Tvkind 列挙型を拡張して新しい値 vkTrike を含めることは、明らかなアプローチのように思えます。しかし、親コンポーネントのコードにアクセスできない、または変更したくない場合はどうすればよいでしょうか?

4

3 に答える 3

4

列挙型の継承は、クラスの場合と同じようには機能しません。これは、コードが列挙についての仮定を作成するため、クラスについては決して行われないためです。たとえば、元の列挙(TSpecialKind)が与えられた場合、サードパーティコンポーネントには次のようなコードが含まれている可能性があります。

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

その列挙の一部ではないものを型にキャストできたとしても、TSpecialKind上記のコードの結果は未定義になります(そして間違いなく良くありません!)

列挙は別の方法で使用される可能性があり、サードパーティコンポーネントがその方法でのみ使用する場合は、「奇妙な」ことができる可能性がありますが、お勧めしません。オリジナルTSpecialKindTSpecialKindsセットタイプでのみ使用され、次のようにのみ使用される場合:

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;
于 2013-02-01T15:06:44.337 に答える
2

元のコンポーネントを変更せずに、必要な変更を加えることが合理的に期待できるとは思いません。

あなたの車の種類の例を取り上げて、もう少し深く掘り下げてみましょう。元のコンポーネントには次のようなコードが含まれていると思います。

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

ここで、追加の列挙型を含む列挙型を導入するとします。

TExtendedVehicleKind = (vkCar, vkCycle, vkTrike);

上記のcaseステートメントがExtendedKindequalで実行された場合vkTrike、メソッドは呼び出されません。

さて、おそらく、元のコントロールから望む動作は、に設定するか、いつに設定KindするvkCarかによって実現できます。しかし、それは私にはありそうもないようです。あなただけがコードを持っているので、あなただけが確実に知ることができ、あなたの実際の問題が何であるかを知っています。vkCycleExtendedKindvkTrike

于 2013-02-01T16:00:18.067 に答える
-2

「プロパティの列挙型を拡張する必要がある」にありました。

クイック最初の提案。列挙型を新しいプロパティ ラッパーとして既存のプロパティに追加します。


潜在的な親クラス コード:


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
   );

乾杯。

于 2013-02-01T18:30:28.387 に答える