2009-08-05 5 views
2

Pourquoi dans le code ci-dessous, j'obtiens le message "Échec" plutôt que "Réussi"La procédure de classe de classe de base doit instancier l'objet d'un descendant?

Contexte: J'aime utiliser des procédures de classe qui instancient leur objet propriétaire, font quelque chose, puis le libèrent.

Cependant, cette approche ne fonctionne pas si j'ai un objet descendant:

Toutes les suggestions sur la façon de fournir des procédures de classe dans une classe de base qui peut être appelé comme un enfant? Est-ce que je pense à tort?

Type 
    TBase = class(TObject) 
    Protected 
     Procedure Proc1; Virtual; 
    Public 
     Class Procedure MyClassProc; 
    end; 

    Class Procedure TBase.MyClassProc; 
    Var 
    Base: TBase; 
    begin 
    Base := TBase.Create; 
    Base.Proc1; 
    Base.Free; 
    end; 

    Procedure TBase.Proc1; 
    begin 
    Assert(FALSE, 'Failed'); 
    end; 

type 
    TChild = class(TBase) 
    protected 
     Procedure Proc1; Override; 
    end; 

    Procedure TChild.Proc1; 
    begin 
    ShowMessage('Succeeded'); 
    end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    TChild.MyClassProc; 
end; 

Répondre

1

Strip tout bas au strict minimum, et vous verrez que vous ne créez jamais une instance TBase, donc par conséquent que TBase.Proc1() sera jamais appelé. Si vous voulez que TChild.Proc1() soit appelé, vous devez créer une instance TChild et laisser le polymorphisme fonctionner.

Il pourrait cependant y avoir de meilleurs moyens d'atteindre votre objectif (quel qu'il soit) que de faire en sorte qu'une méthode de classe crée une instance d'objet pour faire quelque chose. Peut-être devriez-vous clarifier votre question.

+0

@mghie: Bonjour! Au début, j'ai une question comme ça! Laissez-moi vous expliquer: J'ai fait un formulaire de base pour lister certaines propriétés dans le code inspect, avec différentes formes héritées, une de ces propriétés était le type de formulaire qui était enregistré statiquement dans le fichier .FRM et je ne savais pas quel genre de forme était de l'enregistrer dans mon usine de forme de custon. La logique de l'inscription était dans la classe de la Base Form à partager avec ses enfants. La seule façon de retrouver le type de forme était de l'instancier et de l'obtenir à partir de la forme "objet". Cordialement. –

1

Ici, il est

Ajouter

TBase = class; 
TBaseClass = class of TBase; 

TBase = class(TObject) 
protected 
    class function GetBaseClass: TBaseClass; virtual; 

function TBase.GetBaseClass: TBaseClass; 
begin 
    Result := TBase; 
end; 

TChild = class(TBase) 
protected 
    class function GetBaseClass: TBaseClass; override; 

function TChild.GetBaseClass: TBaseClass; 
begin 
    Result := TChild; 
end; 

changement

de

Base := TBase.Create; 

à

Base := GetBaseClass.Create; 

Profitez de votre travail

Cheer

A Pham

+0

C'est une solution de contournement évidente, mais elle soulève encore la question: pourquoi on le ferait de cette façon? Pourquoi la méthode de classe, pourquoi pas une fonction gratuite? Pourquoi avoir une hiérarchie de classe ** du tout **? – mghie

+0

@mghie - J'ai utilisé un modèle similaire à celui-ci dans le passé, mais avec un registre de classe pour déterminer quelle classe créer à l'exécution, ou pour "cloner" la classe actuelle. – skamradt

+0

@skamradt: Le polymorphisme et les méthodes de classe ne vont pas ensemble pour moi. si une instance doit être créée de toute façon, ne la passe pas au lieu de la classe? Comme pour * Clone() * - il ne devrait pas être une méthode de classe, comme une instance est là pour être clonée? – mghie

5

Vous pouvez le faire facilement avec méta-programmation! Changez simplement "TBase.Create" en "Self.Create" "self" représente la classe courante, elle ne metter pas si c'est une base o une classe enfant.

Type 
    TBase = class(TObject) 
    Protected 
     Procedure Proc1; Virtual; 
    Public 
     Class Procedure MyClassProc; 
    end; 

    Class Procedure TBase.MyClassProc; 
    Var 
    MyObject: TBase; 
    begin 
    // MyObject := TBase.Create; 
    MyObject := Self.Create; // The Magic goes here, self is the class that's calling this method, in this case, TChild } 
    MyObject.Proc1; 
    MyObject.Free; 
    end; 

    Procedure TBase.Proc1; 
    begin 
    Assert(FALSE, 'Failed'); 
    end; 

type 
    TChild = class(TBase) 
    protected 
     Procedure Proc1; Override; 
    end; 

    Procedure TChild.Proc1; 
    begin 
    ShowMessage('Succeeded'); 
    end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    TChild.MyClassProc; 
end; 
+1

+1.Je n'aime toujours pas toute l'idée, mais au moins cela vient avec le minimum de code supplémentaire, et il n'y a aucun risque de foirer en oubliant de fournir la fonction auxiliaire surchargée. – mghie

Questions connexes