2009-05-12 9 views
6

Est-il possible en Delphi d'utiliser RTTI (ou autre chose) pour vérifier si une classe est déclarée comme abstraite? Quelque chose comme:Comment vérifier si une classe Delphi est déclarée abstraite?

TMyAbstractClass = class abstract(TObject) 
    // ... 
end; 

... 

if IsAbstract(TMyAbstractClass.ClassInfo) then 
    ShowMessage('Yeah') 
else 
    ShowMessage('Computer says no...'); 
+0

La réponse à cette question http://stackoverflow.com/questions/791004/how-can-i-detect-if-a-delphi-class-has-a-virtual-constructor pourrait vous aider. – RobS

Répondre

0

Un rapide coup d'oeil à travers l'unité TypInfo ne tourne pas quoi que ce soit utile. Je pense que la notion de "classe abstraite" est purement pour le bénéfice du compilateur. Il lui donne une règle à appliquer - pas d'instanciation de cette classe, seulement ses descendants - mais ne fait vraiment rien à l'exécution, donc il n'y a pas besoin d'enregistrer de RTTI pour cela. Pourquoi essaies-tu de trouver ça de toute façon, par simple curiosité?

+0

J'ai une classe dérivée TCollection qui peut contenir différents classtypes (TCollectionItem) avec un ancêtre commun. Pour être précis, il s'agit d'une collection de colonnes de grille qui contient différents types de colonnes. Et maintenant je veux créer un éditeur de collection designtime qui permet à l'utilisateur d'ajouter des types de colonnes enregistrés (en utilisant TClassFinder) .. mais je ne veux pas les classes abtract dans cet éditeur. Mes classes abstraites s'appellent TCustom * et pour l'instant je filtre en utilisant le nom de la classe. – mrMoo

+0

Votre problème, par conséquent, n'est pas de savoir comment détecter les classes abstraites. Votre problème est de savoir comment obtenir une liste de "types de colonnes" valides. La solution facile est de s'assurer que vous n'enregistrez que les classes qui sont des types de colonnes valides. Ne pas enregistrer les classes abstraites en premier lieu. La seule raison d'enregistrer une classe est que vous pouvez la rechercher par nom puis l'instancier. N'enregistrez pas des classes qui ne sont jamais destinées à être instanciées. Vous devriez constater que la VCL n'enregistre pas ses propres classes de base "personnalisées" non plus. –

+0

Oui très vrai, le problème est que je n'enregistre que les classes qui sont "valides", comme vous le suggérez, mais Delphi enregistre automatiquement les classes parentes, par ex. si vous enregistrez TButton en utilisant RegisterClass (TButton), Delphi enregistrera aussi TCustomButton, TButtonControl et ainsi de suite ...Merci quand même pour votre article sur les méthodes abstraites, très intéressant, mais je suppose que la réponse correcte à ma question est "Non" – mrMoo

6

Je n'ai pas une version suffisamment récente pour répondre directement à votre question, mais gardez à l'esprit qu'il ne vraiment importe si la classe est abstraite . Tout ce qui est fait, c'est que le compilateur vous empêche d'appeler un constructeur directement sur la classe. Si vous placez la référence de classe dans une variable de référence de classe, le compilateur vous permettra d'appeler le constructeur sur la variable, et lors de l'exécution, vous aurez une instance de la classe supposée instable.

var 
    c: TClass; 
    o: TObject; 
begin 
    c := TMyAbstractClass; 
    o := c.Create; 
    Assert(o is TMyAbstractClass); 
end; 

Ce qui est vraiment important est si la classe a des méthodes abstraites. Vous pouvez vérifier cela assez facilement. Regardez dans le VMT de la classe. Tout emplacement de méthode virtuelle contenant un pointeur sur System._AbstractError est une méthode abstraite. La partie la plus délicate est de savoir combien d'emplacements de méthodes virtuelles doivent être vérifiés, puisque cela n'est pas enregistré. Allen Bauer demonstrated how to do that dans une réponse à another question, mais dans les commentaires Mason Wheeler souligne qu'il peut retourner des valeurs plus grandes que ce qu'il devrait. Il mentionne la fonction GetVirtualMethodCount du JCL, ce qui devrait donner un nombre plus précis de méthodes virtuelles définies par l'utilisateur. En utilisant cette fonction et GetVirtualMethod, aussi du JCL, nous obtenons cette fonction:

function HasAbstractMethods(c: TClass): Boolean; 
var 
    i: Integer; 
begin 
    Result := True; 
    for i := 0 to Pred(GetVirtualMethodCount(c)) do 
    if GetVirtualMethod(c, i) = @_AbstractError then 
     exit; 
    Result := False; 
end; 

Si une classe abstraite n'a pas de méthode abstraite, alors comment abstraite peut-il vraiment? Il doit avoir été marqué abstrait pour empêcher les développeurs de créer des instances de celui-ci, mais si vous le souhaitez, vous pouvez en créer des instances, donc marquer une classe abstraite est vraiment plus un avertissement qu'une restriction réelle sur l'utilisation.

+0

Malheureusement, lorsque j'ai effectué quelques tests, l'algorithme d'Allen s'est avéré ne pas fonctionner dans tous les cas. Je vais y réfléchir un peu et poster une correction dans cette question si je peux le comprendre. –

+0

Apparemment, d'autres choses peuvent être placées devant le nom. Au lieu de l'algorithme d'Allen, essayez JclSysUtils.GetVirtualMethodCount, qui est situé dans la même unité JCL que GetVirtualMethod. –

+0

En effet. La version JCL vérifie tous les autres pointeurs dans le VMT, et si l'un d'entre eux se produit entre la première méthode virtuelle et le nom de la classe, il "raccourcit" la fenêtre de ce qu'il considère être la gamme de méthodes virtuelles. –

Questions connexes