Comment rendre générique le type de retour d'une méthode?

166

Existe-t-il un moyen de rendre cette méthode générique afin que je puisse renvoyer une chaîne, un booléen, un entier ou un double? Pour le moment, il renvoie une chaîne, mais s'il est capable de trouver "true" ou "false" comme valeur de configuration, je voudrais renvoyer un bool par exemple.

    public static string ConfigSetting(string settingName)
    {  
         return ConfigurationManager.AppSettings[settingName];
    }
MacGyver
la source
Existe-t-il un moyen pour vous de savoir de quel type correspond chaque paramètre?
thecoshman
2
Je pense que la question que vous voulez vraiment vous poser est "Comment faire pour que ma configuration d'application soit fortement typée?" Cela fait trop longtemps que je n'ai pas travaillé avec ça pour écrire une réponse correcte, cependant.
Simon
Yah, idéalement, je ne veux pas avoir à passer le type dans la méthode. Je ne vais avoir que les 4 types que j'ai mentionnés. Donc, si "true" / "false" est défini, je veux que cette fonction renvoie un booléen (sans avoir besoin de le passer dans la méthode), je peux probablement combiner int et double en juste double, et tout le reste devrait être une chaîne. Ce qui est déjà répondu fonctionnera bien, mais je dois passer le type à chaque fois, ce qui est probablement bien.
MacGyver
3
Votre commentaire donne l'impression que vous demandez une méthode qui renverra un bool (ou une chaîne, ou un int, ou autre) fortement typé au moment de l'exécution en fonction des données réelles extraites pour la clé de nom de paramètre. C # ne le fera pas pour vous; il n'y a aucun moyen de connaître le type de cette valeur au moment de la compilation. En d'autres termes, c'est du typage dynamique, pas du typage statique. C # peut le faire pour vous si vous utilisez le dynamicmot - clé. Il y a un coût de performance pour cela, mais pour la lecture d'un fichier de configuration, le coût de performance est presque certainement insignifiant.
phoog

Réponses:

354

Vous devez en faire une méthode générique, comme ceci:

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

Mais l' appelant devra spécifier le type auquel il s'attend. Vous pourriez alors potentiellement utiliser Convert.ChangeType, en supposant que tous les types pertinents sont pris en charge:

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

Je ne suis pas entièrement convaincu que tout cela soit une bonne idée, remarquez ...

Jon Skeet
la source
21
/ * code pour convertir le paramètre en T ... * / et suit ici tout le roman :)
Adrian Iftode
1
Cela ne vous obligerait-il pas à connaître le type de paramètre que vous souhaitez obtenir, ce qui n'est peut-être pas possible.
thecoshman
2
@thecoshman: Ce serait le cas, mais si vous ne l'avez pas fait, que faites-vous de la valeur renvoyée?
George Duckett
5
Bien que cette réponse est bien sûr correcte, et que vous notez satisfait la demande de l' OP, il est probablement utile de mentionner que l'ancienne approche des méthodes distinctes ( ConfigSettingString, ConfigSettingBool, etc.) a l'avantage des corps de la méthode qui sera plus courte, plus claire et mieux ciblée .
phoog
4
Si cela n'est pas recommandé, quel est le but des types de retour génériques?
bobbyalex
29

Vous pouvez utiliser Convert.ChangeType():

public static T ConfigSetting<T>(string settingName)
{
    return (T)Convert.ChangeType(ConfigurationManager.AppSettings[settingName], typeof(T));
}
Verre brisé
la source
13

Il existe de nombreuses façons de procéder (classées par priorité, spécifiques au problème du PO)

  1. Option 1: approche directe - Créez plusieurs fonctions pour chaque type attendu plutôt que d'avoir une fonction générique.

    public static bool ConfigSettingInt(string settingName)
    {  
         return Convert.ToBoolean(ConfigurationManager.AppSettings[settingName]);
    }
  2. Option 2: lorsque vous ne souhaitez pas utiliser de méthodes de conversion sophistiquées - Convertissez la valeur en objet, puis en type générique.

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

    Remarque - Cela générera une erreur si la distribution n'est pas valide (votre cas). Je ne recommanderais pas de faire cela si vous n'êtes pas sûr du type de casting, optez plutôt pour l'option 3.

  3. Option 3: Générique avec sécurité de type - Créez une fonction générique pour gérer la conversion de type.

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

    Remarque - T est le type attendu, notez la contrainte where ici (le type de U doit être IConvertible pour nous sauver des erreurs)

RollerCosta
la source
4
Pourquoi rendre la troisième option générique U? Cela ne sert à rien et cela rend la méthode plus difficile à appeler. Acceptez simplement à la IConvertibleplace. Je ne pense pas qu'il vaut la peine d'inclure la deuxième option pour cette question étant donné qu'elle ne répond pas à la question posée. Vous devriez probablement également renommer la méthode dans la première option ...
Jon Skeet
7

Vous devez convertir le type de votre valeur de retour de la méthode en type générique que vous passez à la méthode lors de l'appel.

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

Vous devez transmettre un type de type castable pour la valeur que vous renvoyez via cette méthode.

Si vous souhaitez renvoyer une valeur qui ne peut pas être convertie en type générique que vous passez, vous devrez peut-être modifier le code ou vous assurer de transmettre un type pouvant être converti en valeur de retour de method. Donc, cette approche n'est pas recommandée.

Vinay Chanumolu
la source
Spot on - pour moi, la dernière ligne return (T)Convert.ChangeType(number, typeof(T));était exactement ce qui me manquait -
bravo
1

Créez une fonction et transmettez le paramètre put en tant que type générique.

 public static T some_function<T>(T out_put_object /*declare as Output object*/)
    {
        return out_put_object;
    }
Prabhakar
la source
C'est en fait assez intelligent pour quelques cas d'utilisation. Comme extraire des données d'une base de données. Vous savez que vous obtiendrez une liste de données de type T. La méthode de chargement ne sait tout simplement pas quel type de T vous voulez maintenant. Alors passez simplement un nouveau List <WantedObject> à ceci et la méthode peut faire son travail et remplir la liste avant de la renvoyer. Agréable!
Marco Heumann le
0

Veuillez essayer le code ci-dessous:

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

J'exécutais cela tout au long de mon code et je voulais un moyen de le mettre dans une méthode. Je voulais partager ceci ici car je n'avais pas besoin d'utiliser le Convert.ChangeType pour ma valeur de retour. Ce n'est peut-être pas une meilleure pratique, mais cela a fonctionné pour moi. Cette méthode prend un tableau de type générique et une valeur à ajouter à la fin du tableau. Le tableau est ensuite copié avec la première valeur supprimée et la valeur prise en compte dans la méthode est ajoutée à la fin du tableau. La dernière chose est que je renvoie le tableau générique.

Adam Howard
la source