2009-10-04 3 views
1

У меня есть массив, который хранит словарь типов:Динамическая Generic декларация типа Т

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

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

Я хотел бы использовать этот словарь, чтобы объявить переменную типа 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; 

Фон что я создаю инфраструктуру распределенного кэширования. У меня очень маленький CachingProvider, который позволяет вам обновлять элемент в кеше.

Я хотел бы разоблачить этот метод как веб-сервис, чтобы все серверы в моей ферме могли обновлять свой кеш. Но я хотел бы иметь только один метод, открытый как веб-сервис, который затем обновляет соответствующий элемент в кеше.

Это метод, который я пытаюсь выставить:

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); 
    } 

Вещи, которые я пробовал: 1) Я попытался обнажая метод непосредственно в качестве службы WCF, но, конечно, не работает, потому что от метода. 2) Я попробовал лить Словарь, который бы нашел, потому что мне не нужно делать ничего с возвращаемым значением, мне просто нужно обновить элемент в кеше. Но это тоже не сработало. Ошибка, которую я получаю: невозможно сбрасывать объект типа «System.Collections.Generic.Dictionary 2[System.Int32,CachingPrototype.CustomerQuickSearch]' to type 'System.Collections.Generic.Dictionary 2 [System.Int32, System.Object] '.

Ваши комментарии были очень полезными и помогли мне ответить на мой вопрос. Решение, которое я придумал, состоит в том, чтобы просто включить мой метод WCF в оператор switch, чтобы я мог вызвать метод UpdateCacheEntryItem с правильным типом T. Поскольку нет способа конвертировать из Type в общий оператор T, это единственный вариант. Так как у меня не так много типов в кеше, это работает очень хорошо. (Другим решением будет использовать интерфейс, как указано ниже, но это не будет так сильно типизированных, как хотелось бы.)

[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"); 
     } 

Спасибо всем за вашу помощь, вы великолепны!

+0

Дубликат http://stackoverflow.com/questions/1437162/dynamic-casting? –

+0

Небольшой контекст будет приятным. Что вы на самом деле пытаетесь достичь? Возможно, вместо этого будет использоваться общий интерфейс или фабричный класс? – tvanfosson

+0

Я смущен - что вы пытаетесь кэшировать - тип или значение? и что вы используете в качестве ключа кеширования? – mfeingold

ответ

11

Идея «динамического объявления переменной» противоречит всей точке того, что существует тип как часть объявления переменной. Идея заключается в том, что вы можете указать компилятору тип, чтобы он мог проверить, что вы делаете. В этом случае вы вообще не указали какую-либо информацию о типе. Вы могли бы просто объявить myVar как тип object; это в основном то же самое, что сказать: «Я почти ничего не знаю о значении myVar, за исключением того, что это ссылка».

Если у вас есть общий интерфейс, конечно, это было бы здорово - и тогда вы могли бы безопасно использовать членов этого интерфейса (после создания/выборки соответствующего экземпляра, конечно). Но в противном случае, вы действительно не можете многое сделать, если не знаете что-то о типе во время компиляции.

В C# 4 вы можете объявить переменную типа dynamic, которая сделает всю привязку динамической - в основном вы можете сделать то, что вам нравится, и все это будет разрешено во время выполнения. Я бы посоветовал использовать статическую типизацию, где бы вы ни находились, так что ошибки могут быть пойманы во время компиляции.

+0

+1 - но я подозреваю, что есть способ решить настоящую проблему, если бы мы знали, что это было на самом деле. – tvanfosson

+0

@tvanfosson: Возможно ... возможно нет. Конечно, есть случаи, когда дженерики просто не позволяют вам выразить то, что вам нужно. Например, у вас не может быть словаря из «Тип» в «экземпляр типа ключа». Вы можете построить один и полагаться на свою целостность, но вы не можете выразить его напрямую с помощью дженериков. –

+0

@ Jon Skeet - Я думаю, что это может быть случай «Я получаю имя того, что хочу от веб-страницы (имя столбца и т. Д.), Теперь я хочу создать или найти один из них», которая разрешима. Возможно, я просто под влиянием моего собственного типичного контекста. Я согласен с идеей использования интерфейса, если это возможно. – tvanfosson

2

Мне кажется, что интерфейс и некоторые кастинги помогут решить вашу проблему. Просто у каждого из ваших классов кэширования реализуется интерфейс. Храните предметы этого типа в своем словаре. Предположительно, CacheRef будет иметь тип Dictionary<CacheKey,Dictionary<CacheKey,ICacheable>>.Осталось только убедиться, что ваши классы кэширования реализуют интерфейс.

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); 
} 

Обратите внимание, что это не так, как говорит @ Джон тарелочкам в своих комментариях к его ответу, соблюдение типа в вашем словаре. Это зависит от вашего кода, чтобы убедиться, что вы помещаете нужные объекты в каждый кеш. Мне было бы комфортно с этим, если бы ваши методы гидратации были покрыты модульными тестами, чтобы гарантировать, что при предоставлении определенного ключа они всегда создают объекты соответствующего типа.

+0

Это хорошее решение, я пробовал это, и он работает. но я решил использовать оператор switch в моем вызове WCF, чтобы я мог продолжать использовать сильную типизацию. – BrokeMyLegBiking