Як зробити тип повернення методу загальним?


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ключове слово. Для цього є вартість продуктивності, але для читання конфігураційного файлу вартість продуктивності майже напевно незначна.
фог

Відповіді:


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 ... * / і тут слід весь роман :)
Адріан Іфтоде

1
Це не вимагає, щоб ви знали тип налаштування, який ви хочете отримати, що може бути неможливим.
thecoshman

2
@thecoshman: Це було б, але якби ти тоді не зробив те, що робиш із поверненим значенням?
Джордж Дакетт

5
Хоча ця відповідь звичайно правильно, і як Ви відзначаєте задовольняє запит OP, це, ймовірно , варто відзначити , що старий підхід окремих методів ( ConfigSettingString, ConfigSettingBoolі т.д.) має перевагу методу тел, буде коротше, ясніше і краще зосереджені .
foog

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 - очікуваний тип, зверніть увагу на обмеження, де тут (тип U повинен бути IConvertible, щоб врятувати нас від помилок)


4
Навіщо робити третій варіант загальним U? Немає сенсу робити це, і це ускладнює виклик методу. Просто прийміть IConvertibleзамість цього. Я не думаю, що варто включати другий варіант цього питання, враховуючи те, що він не відповідає на запитання, яке йому задають. Напевно, ви також повинні перейменувати метод у першому варіанті ...
Джон Скіт

7

Ви повинні перетворити тип повернутого значення методу в загальний тип, який ви передаєте методу під час виклику.

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

Це насправді досить розумно для кількох випадків використання. Як і витягування даних із бази даних. Ви знаєте, що ви отримаєте список даних типу T. Метод завантаження просто не знає, який тип Т ви хочете правильно ЗАРАЗ. Тому просто передайте до цього новий Список <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;
}

-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 для повернення мого значення. Це може бути не найкращою практикою, але це працювало для мене. Цей метод приймає масив загального типу та значення, яке потрібно додати до кінця масиву. Потім масив копіюється з першим позбавленим значенням, а значення, взяте в метод, додається в кінець масиву. Останнє - я повертаю загальний масив.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.