2010-12-11 77 views
8

Je viens d'essayer ma première utilisation de génériques dans Delphi 2009 et je suis perplexe sur la façon d'utiliser un type générique comme entrée de la fonction Supports utilisée pour voir si un objet implémente un interface. J'ai créé un petit échantillon illustrant le problème.Utilisation de la fonction Supports() avec le type d'interface générique

Compte tenu de la fonction des types et utilitaire suivant:

IMyInterface = interface 
['{60F37191-5B95-45BC-8C14-76633826889E}'] 
end; 

TMyObject = class(TInterfacedObject, IMyInterface) 
end; 

class function TFunctions.GetInterface<T>(myObject: TObject): T; 
var 
    specificInterface: T; 
begin 
    // This would compile, but looses the generic capability 
    //Supports(myObject, IMyInterface, specificInterface); 

    // This results in compile errors 
    Supports(myObject, T, specificInterface); 

    result := specificInterface; 
end; 

et l'extrait de code suivant:

class procedure TFunctions.Test; 
var 
    myObject: TMyObject; 
    myInterface: IMyInterface; 
begin 
    myObject := TMyObject.Create; 

    myInterface := GetInterface<IMyInterface>(myObject); 
end; 

je me attends pas de problème mais je reçois les erreurs lors de la compilation suivantes:

[Erreur DCC] GenericExample.pas (37): E2029 '(' attendu mais ',' trouvé [Erreur DCC] Générer icExample.pas (37): E2014 Déclaration prévu, mais l'expression de type « T » trouvé

Je ne suis pas sûr de ce que le compilateur me attend à voir avec le T lorsqu'il est utilisé comme argument réel à la fonction .

J'ai cherché autour d'un peu et n'ai pas été capable de craquer celui-ci. Une partie de moi soupçonne que si je pouvais comprendre comment un nom d'interface est converti en type IID: TGUID lors de la compilation, en utilisant un nom d'interface concret, je pourrais avancer, mais cela m'a également échappé.

Toute aide est très appréciée.

Répondre

7

Il n'y a aucune garantie que T possède un GUID associé, et il n'y a aucun moyen dans le langage d'écrire une contrainte sur le paramètre type pour faire cette garantie. Le nom d'interface est converti en GUID par le compilateur en recherchant le nom dans la table de symboles, obtenant la structure de données du compilateur représentant l'interface et vérifiant le champ correspondant pour le GUID. Mais les génériques ne sont pas comme les templates C++; ils doivent être compilés et vérifiés par type et connus pour fonctionner avec n'importe quel paramètre de type valide, ce qui implique de restreindre le paramètre type dans sa déclaration.

Vous pouvez obtenir le GUID en utilisant RTTI (en vérifiant d'abord que T représente bien une interface) avec quelque chose comme GetTypeData(TypeInfo(T))^.Guid et passez le GUID à Supports de cette façon.

+0

pouvez-vous pas appliquer une contrainte que T doit être une interface spécifiée qui a un GUID? –

+1

S'il doit s'agir d'une interface, le code n'est pas générique. Les supports renverront une référence d'interface; le compilateur ne peut pas transformer génériquement ce que T est. –

+1

Barry, merci pour votre aide. La plupart de mon expérience avec la programmation générique provenait d'un arrière-plan C++, et je m'attendais à ce que le compilateur sache si une interface a un Guid de la même façon qu'il le ferait si le nom de l'interface était fourni directement. C'est tout à fait logique maintenant de savoir que les génériques <> modèles C++. Merci encore. – Chad

3

Pourquoi êtes-vous même dérangé?

Pour utiliser cette TFunctions.GetInterface dont vous avez besoin:

  • une interface
  • une référence d'objet

Si vous avez ceux-ci, vous pouvez simplement appeler Supports() directement:

intf := TFunctions.GetInterface<IMyInterface>(myObject); 

est exactement équivale nt:

Supports(IMyInterface, myObject, intf); 

L'utilisation des médicaments génériques ici est une perte de temps et d'efforts et soulève vraiment la question: « Pourquoi le faire? ».

Il rend simplement les choses plus difficiles à lire (comme c'est souvent le cas avec les génériques) et est plus difficile à utiliser.

Supports() retourne un booléen pratique pour indiquer le succès/échec, que vous devez tester séparément à l'aide de votre emballage:

intf := TFunctions.GetInterface<IMyInterface>(myObject); 
    if Assigned(intf) then 
    // ... 

contre:

if Supports(IMyInterface, myObject, intf) then 
    // We can use intf 

Lors de la création des emballages autour fonctionnalité, il est généralement le cas que le résultat est une amélioration de la lisibilité ou de la facilité d'utilisation.

imho cela échoue sur les deux comptes et vous devriez simplement coller avec le Supports() fonction elle-même.

+0

Mon exemple est distillé à partir du contexte actuel, de poser une question spécifique, et n'est pas illustratif de la conception réelle. – Chad

+2

Ensuite, vous devez fournir au moins un peu du contexte. Sinon, c'est comme demander comment aller de A à B sans mentionner que vous ne pouvez pas conduire et que vous n'avez pas d'argent pour un ticket de bus. Il se peut que même dans le contexte, ce que vous faites ne soit pas la meilleure/la plus facile à faire. C'est très bien de poser une question précise, mais si vous voulez des réponses utiles, il ne sert à rien de jeter un contexte important. – Deltics

+2

Je ne suis pas d'accord avec désaccord. À votre santé. – Chad