Come faccio a rendere generico il tipo restituito di un metodo?


166

C'è un modo per rendere generico questo metodo in modo che io possa restituire una stringa, un valore bool, int o double? In questo momento, sta restituendo una stringa, ma se è in grado di trovare "true" o "false" come valore di configurazione, ad esempio, vorrei restituire un valore bool.

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

C'è un modo per sapere quale tipo è ogni impostazione?
thoshoshman,

2
Penso che la domanda che vuoi veramente porre sia "Come posso rendere la mia applicazione configurata fortemente tipizzata?" È passato troppo tempo da quando ho lavorato con quello per scrivere una risposta adeguata, però.
Simon,

Yah, idealmente non voglio passare il tipo nel metodo. Avrò solo i 4 tipi che ho citato. Quindi, se è impostato "true" / "false", voglio che questa funzione restituisca un valore booleano (senza la necessità di passarlo nel metodo), probabilmente posso combinare int e double in solo double, e tutto il resto dovrebbe essere una stringa. Ciò che risponde già funzionerà bene, ma devo passare il tipo ogni volta, il che probabilmente va bene.
MacGyver

3
Il tuo commento sembra che stai chiedendo un metodo che restituirà un bool fortemente tipizzato (o stringa, o int, o cosa hai) in fase di esecuzione in base ai dati effettivi recuperati per la chiave del nome dell'impostazione. C # non lo farà per te; non è possibile conoscere il tipo di quel valore al momento della compilazione. In altre parole, si tratta di una digitazione dinamica, non di una statica. C # può farlo per te se usi la dynamicparola chiave. C'è un costo prestazionale per questo, ma per la lettura di un file di configurazione, il costo prestazionale è quasi certamente insignificante.
phoog

Risposte:


354

Devi renderlo un metodo generico, come questo:

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

Ma il chiamante dovrà specificare il tipo che si aspettano. È quindi possibile utilizzare potenzialmente Convert.ChangeType, supponendo che siano supportati tutti i tipi rilevanti:

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

Non sono del tutto convinto che tutto ciò sia una buona idea, intendiamoci ...


21
/ * codice per convertire l'impostazione in T ... * / e qui segue l'intero romanzo :)
Adrian Iftode

1
Questo non richiederebbe di conoscere il tipo di impostazione che si desidera ottenere, il che potrebbe non essere possibile.
thoshoshman,

2
@thecoshman: Lo sarebbe, ma se non lo facessi allora cosa fai con il valore restituito?
George Duckett,

5
Anche se questa risposta è ovviamente corretta, e, come si nota soddisfa la richiesta del PO, è probabilmente la pena ricordare che il vecchio approccio di metodi separati ( ConfigSettingString, ConfigSettingBool, ecc) ha il vantaggio di corpi di metodo che sarà più breve, più chiaro, e meglio focalizzata .
phoog

4
Se questo non è raccomandato, qual è lo scopo dei tipi di restituzione generici?
bobbyalex,

29

Puoi usare Convert.ChangeType():

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

13

Esistono molti modi per farlo (elencati per priorità, specifici per il problema del PO)

  1. Opzione 1: approccio diretto - Crea più funzioni per ogni tipo che ti aspetti piuttosto che avere una funzione generica.

    public static bool ConfigSettingInt(string settingName)
    {  
         return Convert.ToBoolean(ConfigurationManager.AppSettings[settingName]);
    }
  2. Opzione 2: quando non si desidera utilizzare metodi di conversione elaborati, eseguire il cast del valore su object e quindi su tipo generico.

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

    Nota: questo genererà un errore se il cast non è valido (il tuo caso). Non consiglierei di farlo se non sei sicuro del tipo di casting, piuttosto vai all'opzione 3.

  3. Opzione 3: generico con sicurezza del tipo : crea una funzione generica per gestire la conversione del tipo.

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

    Nota - T è il tipo previsto, annota qui il vincolo (il tipo di U deve essere IConvertible per salvarci dagli errori)


4
Perché rendere generica la terza opzione U? Non ha senso farlo e rende il metodo più difficile da chiamare. Accetta IConvertibleinvece. Non credo valga la pena includere la seconda opzione per questa domanda dato che non risponde alla domanda che viene posta. Probabilmente dovresti anche rinominare il metodo nella prima opzione ...
Jon Skeet il

7

Devi convertire il tipo del valore di ritorno del metodo nel tipo generico che passi al metodo durante la chiamata.

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

È necessario passare un tipo cast di tipo per il valore restituito tramite tale metodo.

Se si desidera restituire un valore che non è cast castabile al tipo generico che si passa, potrebbe essere necessario modificare il codice o assicurarsi di passare un tipo castable per il valore restituito del metodo. Quindi, questo approccio non è raccomandato.


Perfetto - per me l'ultima riga return (T)Convert.ChangeType(number, typeof(T));era esattamente quello che mi mancava - evviva
Greg Trevellick,

1

Creare una funzione e passare il parametro put di tipo generico.

 public static T some_function<T>(T out_put_object /*declare as Output object*/)
    {
        return out_put_object;
    }

Questo è in realtà abbastanza intelligente per alcuni casi d'uso. Come estrarre i dati da un database. Sai che otterrai un elenco di dati di tipo T. Il metodo di caricamento non è in grado di sapere quale tipo di T desideri ORA. Quindi basta passare un nuovo Elenco <WantedObject> a questo e il metodo può fare il suo lavoro e riempire l'elenco prima di restituirlo. Bello!
Marco Heumann,

0

Si prega di provare sotto il codice:

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

Lo stavo eseguendo in tutto il mio codice e volevo un modo per inserirlo in un metodo. Volevo condividere questo qui perché non ho dovuto usare Convert.ChangeType per il mio valore di ritorno. Questa potrebbe non essere una buona pratica ma ha funzionato per me. Questo metodo accetta una matrice di tipo generico e un valore da aggiungere alla fine della matrice. L'array viene quindi copiato con il primo valore rimosso e il valore preso nel metodo viene aggiunto alla fine dell'array. L'ultima cosa è che restituisco l'array generico.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.