Как сделать тип возвращаемого значения для метода универсальным?

166

Есть ли способ сделать этот метод универсальным, чтобы я мог вернуть строку, bool, int или double? Прямо сейчас он возвращает строку, но если он может найти «true» или «false» в качестве значения конфигурации, я хотел бы, например, вернуть bool.

    public static string ConfigSetting(string settingName)
    {  
         return ConfigurationManager.AppSettings[settingName];
    }
МакГайвер
источник
Есть ли способ узнать, какой тип у каждой настройки?
Thecoshman
2
Я думаю, что вопрос, который вы действительно хотите задать, это «Как мне сделать конфигурацию моего приложения строго типизированной?» Впрочем, я слишком долго работал с этим, чтобы написать правильный ответ.
Симон
Да, в идеале я не хочу передавать тип в метод. У меня будут только 4 типа, которые я упомянул. Поэтому, если установлено «true» / «false», я хочу, чтобы эта функция возвращала логическое значение (без необходимости передавать его в метод), я, вероятно, могу объединить int и double в просто double, а все остальное должно быть строкой. Ответы уже будут работать нормально, но мне нужно каждый раз передавать тип, что, вероятно, хорошо.
MacGyver
3
Ваш комментарий звучит так, будто вы запрашиваете метод, который будет возвращать типизированный тип bool (или строку, или int, или что у вас) во время выполнения на основе фактических данных, полученных для ключа имени параметра. C # не сделает этого за вас; нет никакого способа узнать тип этого значения во время компиляции. Другими словами, это динамическая типизация, а не статическая. C # может сделать это за вас, если вы используете dynamicключевое слово. Для этого есть затраты на производительность, но для чтения конфигурационного файла, производительность почти наверняка незначительна.
Phoog

Ответы:

354

Вам нужно сделать его универсальным методом, например так:

public static T ConfigSetting<T>(string settingName)
{  
    return /* code to convert the setting to T... */
}

Но звонивший должен будет указать ожидаемый тип. Затем вы могли бы потенциально использовать Convert.ChangeType, предполагая, что поддерживаются все соответствующие типы:

public static T ConfigSetting<T>(string settingName)
{  
    object value = ConfigurationManager.AppSettings[settingName];
    return (T) Convert.ChangeType(value, typeof(T));
}

Я не совсем уверен, что все это хорошая идея, заметьте ...

Джон Скит
источник
21
/ * код для преобразования настроек в T ... * / и здесь следует весь роман :)
Adrian Iftode
1
Если бы для этого не требовалось знать тип настройки, которую вы хотите получить, что может оказаться невозможным.
Thecoshman
2
@thecoshman: Да, но если нет, то что вы делаете с возвращенным значением?
Джордж Дакетт
5
Хотя этот ответ конечно правильно, и как Вы отмечаете удовлетворяет запрос OP, это, вероятно , стоит отметить , что старый подход отдельных методов ( ConfigSettingString, ConfigSettingBoolи т.д.) имеет преимущество метода тел, будет короче, яснее и лучше сосредоточены ,
Phoog
4
Если это не рекомендуется, то какова цель универсальных типов возвращаемых данных?
bobbyalex
29

Вы можете использовать Convert.ChangeType():

public static T ConfigSetting<T>(string settingName)
{
    return (T)Convert.ChangeType(ConfigurationManager.AppSettings[settingName], typeof(T));
}
Разбитое стекло
источник
13

Есть много способов сделать это (перечислены в порядке приоритета, в зависимости от проблемы ОП)

  1. Вариант 1. Прямой подход - создайте несколько функций для каждого ожидаемого типа, а не одну общую функцию.

    public static bool ConfigSettingInt(string settingName)
    {  
         return Convert.ToBoolean(ConfigurationManager.AppSettings[settingName]);
    }
  2. Вариант 2. Если вы не хотите использовать причудливые методы преобразования, приведите значение к объекту, а затем к универсальному типу.

    public static T ConfigSetting<T>(string settingName)
    {  
         return (T)(object)ConfigurationManager.AppSettings[settingName];
    }

    Примечание. Это приведет к ошибке, если приведение неверно (ваш случай). Я не рекомендовал бы делать это, если вы не уверены в приведении типов, лучше перейдите к варианту 3.

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

    public static T ConvertValue<T,U>(U value) where U : IConvertible
    {
        return (T)Convert.ChangeType(value, typeof(T));
    } 

    Примечание - T является ожидаемым типом, обратите внимание на ограничение where (тип U должен быть IConvertible, чтобы избавить нас от ошибок)

RollerCosta
источник
4
Зачем делать третий вариант общего U? В этом нет никакого смысла, и это затрудняет вызов метода. Просто примите IConvertibleвместо этого. Я не думаю, что стоит включить второй вариант для этого вопроса, поскольку он не отвечает на заданный вопрос. Вероятно, вам также следует переименовать метод в первом варианте ...
Джон Скит
7

Вы должны преобразовать тип возвращаемого значения метода в тип Generic, который вы передаете методу во время вызова.

    public static T values<T>()
    {
        Random random = new Random();
        int number = random.Next(1, 4);
        return (T)Convert.ChangeType(number, typeof(T));
    }

Вам нужно передать тип, который можно преобразовать в тип для значения, возвращаемого этим методом.

Если вы хотите вернуть значение, которое не может быть преобразовано в тип к передаваемому вами универсальному типу, вам, возможно, придется изменить код или убедиться, что вы передаете тип, который является кастуемым, для возвращаемого значения метода. Таким образом, этот подход не рекомендуется.

Винай Чанумолу
источник
Пятно на - для меня последняя строка return (T)Convert.ChangeType(number, typeof(T));была именно то, что я пропустил - ура
Грег
1

Создайте функцию и передайте параметр put общего типа.

 public static T some_function<T>(T out_put_object /*declare as Output object*/)
    {
        return out_put_object;
    }
Prabhakar
источник
Это на самом деле довольно умно для нескольких вариантов использования. Как вытащить данные из базы данных. Вы знаете, что получите список данных типа T. Метод загрузки просто не знает, какой тип T вы хотите прямо сейчас. Поэтому просто передайте ему новый List <WantedObject>, и метод сможет выполнить свою работу и заполнить список перед его возвратом. Ницца!
Марко Хойманн
0

Пожалуйста, попробуйте ниже код:

public T? GetParsedOrDefaultValue<T>(string valueToParse) where T : struct, IComparable
{
 if(string.EmptyOrNull(valueToParse))return null;
  try
  {
     // return parsed value
     return (T) Convert.ChangeType(valueToParse, typeof(T));
  }
  catch(Exception)
  {
   //default as null value
   return null;
  }
 return null;
}
Sid
источник
-1
 private static T[] prepareArray<T>(T[] arrayToCopy, T value)
    {
        Array.Copy(arrayToCopy, 1, arrayToCopy, 0, arrayToCopy.Length - 1);
        arrayToCopy[arrayToCopy.Length - 1] = value;
        return (T[])arrayToCopy;
    }

Я выполнял это по всему коду и хотел, чтобы это было в методе. Я хотел поделиться этим здесь, потому что мне не нужно было использовать Convert.ChangeType для моего возвращаемого значения. Возможно, это не лучшая практика, но это сработало для меня. Этот метод принимает массив универсального типа и значение, добавляемое в конец массива. Затем массив копируется с удалением первого значения, а значение, принятое в метод, добавляется в конец массива. Последнее, что я возвращаю общий массив.

Адам Ховард
источник