2009-10-04 8 views
1

J'ai un tableau qui stocke un dictionnaire des types:déclaration générique dynamique de type T

//The dictionary: 
Dictionary<CacheKey,Type> TypeLookup; 

//This is the enum: 
public enum CacheKey 
{ 
    UserProfile, 
    CustomerQuickSearch, 
    CommissionConfiguration 
} 

Je voudrais utiliser ce dictionnaire pour déclarer une variable de type T

 //instead of 
     T myvar; 

     //I want to dynamically declare myvar as: 
     //1)get the type for the cacheKey from the dictionary: 
     Type type = TypeLookup[cacheKey]; 
     //2)declare myvar as the corresponding Type: 
     type myvar; 

L'arrière-plan est que je construis une infrastructure de caching distribué. J'ai un super petit CachingProvider qui vous permet de mettre à jour un élément dans le cache.

Je voudrais exposer cette méthode en tant que service web afin que tous les serveurs de ma ferme puissent avoir leur cache mis à jour. Mais je voudrais avoir une seule méthode exposée en tant que webservice qui met alors à jour l'élément correspondant dans le cache.

C'est la méthode que je suis en train d'exposer:

public static void UpdateCacheEntryItem<T>(CacheKey cacheKey, int id) 
    { 
     //look up the cacheEntry in cache which is a dictionary. 
     Dictionary<int, T> cacheEntry = (Dictionary<int, T>) CacheRef[cacheKey.ToString()]; 

     //call the corresponding method which knows how to hydrate that item and pass in the id. 
     cacheEntry[id] = (T)HydrateCacheEntryItemMethods[cacheKey].Invoke(id); 
    } 

choses que j'ai essayé: 1) J'ai essayé d'exposer la méthode directement en tant que service WCF mais bien sûr, cela ne fonctionne pas parce que de la sur la méthode. 2) J'ai essayé de lancer le dictionnaire qui serait trouvé parce que je n'ai pas besoin de faire quoi que ce soit avec la valeur de retour, j'ai juste besoin de mettre à jour l'élément dans le cache. Mais cela n'a pas fonctionné non plus. Erreur que j'obtiens: Impossible de lancer l'objet de type 'System.Collections.Generic.Dictionary 2[System.Int32,CachingPrototype.CustomerQuickSearch]' to type 'System.Collections.Generic.Dictionary 2 [System.Int32, System.Object]'.

Vos commentaires ont été très utiles et m'ont aidé à répondre à ma question. La solution que j'ai trouvée est simplement d'enrouler ma méthode de service WCF dans une instruction switch afin que je puisse appeler la méthode UpdateCacheEntryItem avec le bon type de T. Comme il n'y a aucun moyen de convertir du type à l'opérateur T générique, c'est la seule option. Comme je n'ai pas beaucoup de types dans Cache, cela marche plutôt bien. (L'autre solution serait d'utiliser une interface comme indiqué ci-dessous, mais qui ne serait pas aussi fortement typé que je le voudrais.)

[OperationContract] 
    public void UpdateCacheEntryItem(CacheKey cacheKey, int id) 
    { 
     switch (cacheKey) 
     { 
      case CacheKey.UserProfile: 
       CacheProvider.UpdateCacheEntryItem<UserProfile>(cacheKey, id); 
       break; 
      case CacheKey.CommissionConfig: 
       CacheProvider.UpdateCacheEntryItem<CommissionConfig>(cacheKey, id); 
       break; 
      case CacheKey.CustomerQuickSearch: 
       CacheProvider.UpdateCacheEntryItem<CustomerQuickSearch>(cacheKey, id); 
       break; 
      default: 
       throw new Exception("Invalid CacheKey"); 
     } 

Merci à tous pour votre aide, vous êtes génial!

+0

Dupliquer de http://stackoverflow.com/questions/1437162/dynamic-casting? –

+0

Un petit contexte serait bien. Qu'est-ce que vous essayez réellement d'accomplir? Peut-être qu'une interface commune ou une classe d'usine serait appropriée à la place? – tvanfosson

+0

Je suis confus - qu'est-ce que vous essayez de mettre en cache - le type ou la valeur? et qu'utilisez-vous comme clé de cache? – mfeingold

Répondre

11

L'idée de "déclarer dynamiquement une variable" est contraire au fait qu'il existe un type dans la déclaration d'une variable. L'idée est que vous pouvez dire au compilateur le type, afin qu'il puisse vérifier ce que vous faites. Dans ce cas, vous n'avez exprimé aucune information sur le type. Vous pourriez aussi bien déclarer myVar comme étant de type object; c'est à peu près la même chose que de dire "Je ne sais pratiquement rien de la valeur de myVar, sauf que c'est une référence."

Si vous avez une interface commune bien sûr, ce serait génial - et vous pourriez utiliser les membres de cette interface en toute sécurité (après avoir créé/récupéré une instance appropriée, bien sûr). Mais sinon, il n'y a vraiment pas beaucoup que vous pouvez faire à moins de savoir quelque chose sur le type au moment de la compilation. En C# 4, vous pouvez déclarer la variable de type dynamic qui rendra toute la liaison dynamique - vous pouvez faire à peu près ce que vous voulez avec, et tout sera résolu au moment de l'exécution. Je vous conseille d'utiliser la frappe statique partout où vous le pouvez, afin que les erreurs puissent être détectées au moment de la compilation.

+0

+1 - mais je soupçonne qu'il existe un moyen de résoudre le problème réel si seulement nous savions ce que c'était vraiment. – tvanfosson

+0

@tvanfosson: Peut-être ... peut-être pas. Il y a certainement des cas où les génériques ne vous permettent simplement pas d'exprimer ce dont vous avez besoin. Par exemple, vous ne pouvez pas avoir un dictionnaire de 'Type' à" une instance du type de clé ". Vous pouvez en construire un et compter sur sa propre intégrité, mais vous ne pouvez pas l'exprimer directement avec des génériques. –

+0

@Jon Skeet - Je pense que cela peut être un cas de "Je reçois le nom de la chose que je veux à partir d'une page web (nom de la colonne, etc.), maintenant je veux créer ou rechercher l'un d'entre eux" ce qui est solvable. Peut-être, je suis juste influencé par mon propre contexte typique, cependant. Je suis d'accord avec l'idée d'utiliser une interface si possible. – tvanfosson

2

Il me semble qu'une interface et un casting vont résoudre votre problème. Faites en sorte que chacune de vos classes pouvant être mises en cache implémentent une interface. Stockez les éléments de ce type dans votre dictionnaire. Vraisemblablement, CacheRef serait de type Dictionary<CacheKey,Dictionary<CacheKey,ICacheable>>.Tout ce qui reste est de s'assurer que vos classes pouvant être mises en cache implémentent l'interface.

public interface ICacheable 
{ 
} 

public static void UpdateCacheEntryItem(CacheKey cacheKey, int id) 
{ 
    //look up the cacheEntry in cache which is a dictionary. 
    Dictionary<CacheKey,ICacheable> cacheEntry = CacheRef[cacheKey.ToString()]; 

    //call the corresponding method which knows how to hydrate that item and pass in the id. 
    cacheEntry[id] = (ICacheable)HydrateCacheEntryItemMethods[cacheKey].Invoke(id); 
} 

Notez que ce n'est pas, comme dit dans Skeet @ Jon ses commentaires à sa réponse, l'application du type dans votre dictionnaire. C'est à votre code de s'assurer que vous mettez le bon type d'objets dans chaque cache. Je serais à l'aise avec cela à condition que vos méthodes d'hydratation soient couvertes par des tests unitaires pour s'assurer que lorsque vous avez une clé donnée, ils produisent toujours des objets du type approprié.

+0

c'est une bonne solution, j'ai essayé ceci et cela fonctionne. mais j'ai décidé d'utiliser une instruction switch dans mon appel WCF afin que je puisse continuer à utiliser la frappe forte. – BrokeMyLegBiking

Questions connexes