Динамическое общее объявление типа T - PullRequest
1 голос
/ 04 октября 2009

У меня есть массив, в котором хранится словарь типов:

//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, это единственный вариант. Поскольку у меня не так много типов в Cache, это работает довольно хорошо. (Другое решение состоит в том, чтобы использовать интерфейс, как указано ниже, но он не будет напечатан так сильно, как хотелось бы.)

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

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

Ответы [ 2 ]

10 голосов
/ 04 октября 2009

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

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

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

2 голосов
/ 04 октября 2009

Мне кажется, что интерфейс и некоторые кастинги решат вашу проблему. Просто пусть каждый из ваших кэшируемых классов реализует интерфейс. Храните элементы этого типа в вашем словаре. Предположительно, 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);
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...