2009-12-07 4 views
3

J'ai besoin d'implémenter une fonction qui retourne un TDictionary, sans spécifier les types exacts. La valeur retournée pourrait être un TDictionary<string,Integer>, TDictionary<string,string> ou TDictionary<string,Boolean>Est-il sécuritaire de lancer des génériques en Delphi?

ce que je pourrais déclarer la fonction avec TDictionary comme paramètre de résultat:

function GetMap: TDictionary; 

puis jeter la valeur de retour:

type 
    TMyMapType: TDictionary<string,Integer>; 
var 
    MyMap: TMyMapType: 
begin 
... 
    MyMap := GetMap as TMyMapType; 
... 
end; 

Edit: trouvé Il semble qu'il n'y ait aucun moyen de déclarer un type de paramètre résultat "générique" qui serait compatible avec mes trois types de dictionnaire.

On dirait que je besoin de quelque chose comme

type 
     TMyMapType: TDictionary<string, ?>; 

qui est pas (encore?) Possible dans le langage Pascal Objet. En Java, il serait quelque chose comme ceci:

static Map<String, Integer>getIntegerMap() { 
    Map<String, Integer> result = new TreeMap<String, Integer>() {}; 
    result.put("foo", Integer.valueOf(42)); 
    return result;   
} 

static Map<String, ?> getMap() { 
    return getIntegerMap(); 
} 

public static void main(String[] args) { 
    System.out.println(getMap().get("foo")); 
} 

Répondre

2

Non, il n'y a pas de "classe TDictionary de base que toutes les versions TDictionary descendent". Les types de paramètres font partie du type de classe. La classe parente de TDictionary<T, U> est TEnumerable<TPair<T, U>> et la classe parente est TObject.

Ceci est un peu gênant, mais il est nécessaire de préserver la sécurité de type. Disons que vous avez un TDictionary<string, TMyObject>, et vous l'avez passé à une fonction qui attend un TDictionary<string, TObject>. Vous pouvez vous attendre à ce que cela fonctionne, puisque vous pouvez passer un TMyObject à un paramètre TObject. Mais ce n'est pas le cas, et il y a une bonne raison. Le compilateur ne peut pas vérifier le type réel dans la fonction de réception à la compilation, donc rien n'empêche la routine de prendre votre dictionnaire et d'appeler .Add(Self.Name, Self), où Self est un TForm (ou autre chose) et non un TMyObject . Puisque toutes les références d'objets sont sizeof (pointeur), cela semble fonctionner, mais quand vous le récupérez dans votre code qui sait ce que le second paramètre est censé être, vous avez un gros problème.

Il existe des moyens de faire en sorte que Generics fonctionne comme vous le souhaitez sans supprimer la sécurité du type, en plaçant des contraintes sur la fonction de réception, mais Delphi ne l'implémente pas actuellement. Delphi Prism fait, et j'ai essayé d'obtenir l'équipe Delphi pour l'implémenter dans la prochaine version, mais nous devrons voir ...

+0

Merci pour cette réponse, après l'avoir lu et http://www.ibm.com/developerworks/java/library/j-jtp04298.html (qui montre une solution possible en utilisant 'help helpers') Je comprends le problème.Dans mon cas simple, une TDictionary avec des objets wrapper primitifs pourrait être une solution de contournement car j'en ai besoin uniquement pour les types de retour. – mjn

0

Lorsque vous appelez GetMap, vous savez déjà que le résultat sera un TDictionary. Pourquoi ne pas créer une fonction générique GetMap de T et U qui retourne un TDictionary de T et U?

Comme ceci:

function GetMap<T, U>: TDictionary<T, U>; 

Ensuite, vous pouvez le faire sans casting:

var 
    MyMap: TMyMapType; 
begin 
    MyMap := GetMap<string, integer>(); 
+0

fonction GetMap : TDictionary ; ne compile pas - et fonctionne GetMap: TDictionary; ne compile pas aussi. Je suis maintenant en mode de lecture tutoriel à nouveau :) – mjn

+1

Cela devrait fonctionner à mon humble avis si 'GetMap' est une méthode de membre. – jpfollenius

Questions connexes