Хобрук: Ваш путь к мастерству в программировании

Может ли делегат принимать дженерик в качестве параметра

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

Этот класс имеет восемь методов, все с почти идентичным кодом, за исключением функции, которую они вызывают. Я создал функцию под названием GetObject, которая вызывает делегата, если не может найти элемент в классе.

Я не могу скомпилировать свой код из-за следующей ошибки:

Аргумент 2: невозможно преобразовать 'System.Collections.Generic.List<string>' в 'MyFunction<System.Collections.Generic.List<string>>'.

Я делаю что-то не так или пытаюсь сделать что-то невозможное?

Вот код, который я пытаюсь.

public delegate T MyFunction<T>(string s); 

public T GetCultures<T>(string s) where T : class {
    return NewListOfStrings(s) as T;
}


public List<string> NewListOfStrings(string s) {
    return new List<string> { s };
}


public List<string> GetListOfStrings(string sitename) {
    string key = cachingService.CreateValidKey("stringvalue");

     //This is the line that fails to compile
     var foundItems = GetObject<List<string>>(key, 
                                      GetCultures<List<string>>(sitename));

     return foundItems;
}



public T GetObject<T>(string key, MyFunction<T> f) where T : class {

    T foundItems = (T)cachingService.GetCachedItem(key);

    if (foundItems == null) {
        lock (key) {
            foundItems = (T)cachingService.GetCachedItem(key);

            if (foundItems == null) {
                foundItems = f as T;

                if (foundItems != null) {
                     cachingService.SetCachedItem(key, foundItems, 5, 
                                                        Constants.MINUTES);
                }
            }
         }
     }

     return foundItems;
 }

Решение

public T GetObject<T>(string key, Func<T> getFromRepository) where T : class {

    T foundItems = (T)cachingService.GetCachedItem(key);

    if (foundItems == null) {
        lock (key) {
           foundItems = (T)cachingService.GetCachedItem(key);

           if (foundItems == null) {
               foundItems = getFromRepository() as T;

               if (foundItems != null) {
                   cachingService.SetCachedItem(key, foundItems, 5, 
                                                      Constants.MINUTES);
               }
            }
        }
    }

    return foundItems;
}


public AreaModels.Site GetSiteByName(string sitename) {
   string key = cachingService.CreateValidKey(
                                 string.Format("Site_{0}", sitename));

   return GetObject<AreaModels.Site>(key, 
                                 () => efRepository.GetSiteByName(sitename));
}


public List<AreaModels.Culture> GetCulturesForSite(string sitename) {
   string key = cachingService.CreateValidKey(
                                  string.Format("Cultures_{0}", sitename));

   return GetObject<List<AreaModels.Culture>>(key, 
                       () => efRepository.GetCulturesForSite(sitename));
}


public List<AreaModels.Resource> Resources(string sitename, int appId) {
   string key = cachingService.CreateValidKey(
                                 string.Format("ResourcesFor{0}", sitename));

   return GetObject<List<AreaModels.Resource>>(key, 
               () => efRepository.GetResourcesBySiteAndAppId(sitename, appId));
}
15.05.2014

  • Да, вы делаете что-то не так: вы передаете список строк методу, который ожидает строку MyFunction‹List‹››. Вы делаете неправильно и другие вещи, например foundItems = f as T;. Это должно быть foundItems = f(someString); или foundItems = f.Invoke(someString); 15.05.2014
  • Я не верю, что вы можете передать параметр, который объявляет общий тип - в этот момент он должен быть конкретным. Вот... public T GetObject<T>(string key, MyFunction<T> f) where T : class 15.05.2014
  • @Daniel Чтобы прокомментировать phoogs, ваша строка вызывает метод GetCultures, что означает, что результат передается GetObject, вам просто нужно указать GetCultures в качестве группы методов, но это означает, что sitename также нужно будет передать отдельно: GetObject<List<string>>(key, GetCultures<List<string>>); // sitename needs to be in here somewhere. 15.05.2014
  • возможно, лучше использовать T GetObject<T>(string key, T f), в любом случае вы используете этот параметр f как значение, а не как функцию 15.05.2014

Ответы:


1

Вы передаете результат функции, а не саму функцию. Вы можете использовать лямбду так:

var foundItems = GetObject<List<string>>(key, 
                                  name => GetCultures<List<string>>(sitename));

У вас также есть эта строка:

foundItems = f as T;

Здесь вы пытаетесь привести саму функцию к типу возвращаемого значения, что не сработает. Вместо этого вы можете сделать:

foundItems = f(name);

Но теперь ваша проблема в том, что вам придется передать имя в GetObject, потому что иначе оно не будет доступно там, где оно нужно. Причина этого в том, что существует несоответствие между MyFunction, которая принимает string, и тем, что вы на самом деле хотите, то есть функцией, которую можно вычислить в GetObject без необходимости передачи параметра имени.

Итак, что вы действительно должны сделать, это изменить своего делегата на:

public delegate T MyFunction<T>(); 

Или, в качестве альтернативы, вообще избавиться от делегата и сделать параметр f равным Func<T>.

С любым из этих вариантов вы можете передать лямбу без каких-либо параметров:

var foundItems = GetObject<List<string>>(key, 
                                  () => GetCultures<List<string>>(sitename));

И оцените это так:

foundItems = f();

Обратите внимание, что создание лямбда-выражения для передачи его в другой метод только для того, чтобы затем оценить его, является немного окольным путем, а не для прямой передачи результата. Поэтому, если нет какой-либо причины, по которой вам нужно сделать это в некоторых случаях, вы можете вместо этого изменить параметр f, чтобы вместо этого принять тип T. В этом случае я подозреваю, что вы делаете это для ленивой оценки функции, чтобы вам не приходилось оценивать, кэширован ли уже результат. Это, вероятно, будет уважительной причиной, если вы не оптимизируете производительность преждевременно.

15.05.2014
  • Это скомпилируется, но обратите внимание, что в теле он попытается преобразовать x => GetCultures<List<string>>(sitename) в List<string>, что не сработает. 15.05.2014
  • Верно, ему нужно заменить foundItems = f as T на foundItems = f() as T;, если он этого хочет. 15.05.2014
  • И часть as T не нужна, так как Function<T> (я думаю) все равно возвращает T. 15.05.2014
  • Здравствуйте, Бен, спасибо за ваш ответ и правки. Это все очень ценится. Я бы не стал передавать ключевой параметр функции. Я использую ключ, чтобы посмотреть в кэше. Если там ничего нет, я вызываю функцию, и параметры, которые принимает функция, определяются в строке, настроенной с помощью GetListOfStrings. 15.05.2014
  • @DanielHollinrake В этом случае вы можете сделать лямбду, например () => GetCultures<List<string>>(sitename)), если вы изменили MyFunction, чтобы не принимать никаких параметров. В качестве альтернативы вы можете сделать что-то вроде name => GetCultures<List<string>>(name)), а затем указать имя в качестве дополнительного параметра для GetObject. 15.05.2014
  • @DanielHollinrake По сути, если вы хотите передать функцию, которая принимает строковый параметр, в GetObject, а затем оценить его в GetObject, вам нужно убедиться, что к строке, которую вы хотите передать, можно получить доступ из GetObject. 15.05.2014
  • @BenAaronson Привет, Бен, спасибо за ваши комментарии. Я попытался запустить свой код и обнаружил, что он не вызывает функцию, которую я передал. Ваш последний комментарий указывает мне правильное направление. Спасибо за вашу помощь. 15.05.2014
  • Я отметил это как принятый ответ, поскольку он помог мне добиться того, что мне нужно было сделать. Я немного опубликую обновление к своему ответу. 15.05.2014

  • 2

    Вы не создаете делегата. Фактически вы оцениваете метод перед вызовом GetObject. Легко исправить:

        var foundItems = GetObject<List<string>>(key,
            name => GetCultures<List<string>>(name));
    

    Также обратите внимание, что не очевидно, что вы хотите делать с sitename в этом сценарии; вместо этого вы можете иметь в виду следующее:

            name => GetCultures<List<string>>(sitename));
    
    15.05.2014
  • Спасибо за это. Выглядит хорошо до сих пор. Я попробую со своим реальным кодом, а не с фиктивным кодом, и дам вам знать, как у меня дела. 15.05.2014
  • Это помогло мне скомпилировать мой код. Спасибо. Я отметил ответ Бена как тот, который я принимаю, поскольку он помог мне достичь моей цели. 15.05.2014

  • 3

    Вот полный пример

    public class TestDelegate
    {
        //You don't need generic here if you always return a list of string
        public List<string> GetCulture(string s) 
        {
            return new List<string> { s };
        }
    
        public T GetObject<T>(string key, Func<string, T> fn)
        {
            T foundItems = fn(key);
            return foundItems;
        }
    
        public void Test()
        {
            List<string> test = GetObject("abc", x => GetCulture(x));
        }
    }
    

    Если вы посмотрите на методы Test() и GetObject(), то сможете заметить 3 интересных момента:

    1. Вам не нужно указывать общий тип в GetObject(), потому что компилятор выводит его из GetCulture().
    2. Параметр x служит входом для вашей функции делегата, поэтому метод GetObject может использовать «ключ» и передать его функции делегата.
    3. Я заменяю вашу функцию делегата на «Func» со строковым вводом и выводом списка.
    15.05.2014
    Новые материалы

    Управление DOM для чайников вроде меня
    Одной из первых вещей, которую мы рассмотрели, когда начали изучать Javascript во Flatiron, была модель DOM. Кто он? Чем он занимается? Он больше машина, чем человек? Ну да довольно много. ДОМ..

    Что такое структура данных?
    Структура данных хранит и извлекает данные. Все, что обеспечивает эти две функции, является структурой данных . Период. Вы можете пропустить оставшуюся часть статьи, если ответ..

    мои январские чтения по программированию
    Эрик Эллиот Программирование приложения JavaScript Эл Свейгарт «Автоматизируйте скучные вещи с помощью Python» Прогрессивное веб-приложение Google..

    Создание ассоциаций секвелизации с помощью инструмента командной строки Sequelize
    Sequelize - популярный, простой в использовании инструмент объектно-реляционного сопоставления (ORM) JavaScript, который работает с базами данных SQL. Довольно просто начать новый проект с..

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

    Введение в машинное обучение для обнаружения аномалий (часть 1)
    Тщательно созданный, тщательно спроектированный ресурс для специалистов по данным. Часть 1 Главы 03 из Руководства по машинному обучению для обнаружения аномалий Внимание! Прежде чем вы..

    Начало работы с Pulumi в Digital Ocean
    Цифровой океан (ДО) — отличная альтернатива многим другим поставщикам облачных услуг. DO предоставляет простой и понятный пользовательский интерфейс, упрощающий управление инфраструктурой и..