¿Cómo hago que el tipo de devolución de un método sea genérico?

166

¿Hay alguna manera de hacer que este método sea genérico para que pueda devolver una cadena, bool, int o double? En este momento, está devolviendo una cadena, pero si puede encontrar "verdadero" o "falso" como el valor de configuración, me gustaría devolver un bool, por ejemplo.

    public static string ConfigSetting(string settingName)
    {  
         return ConfigurationManager.AppSettings[settingName];
    }
MacGyver
fuente
¿Hay alguna manera de saber de qué tipo es cada configuración?
thecoshman
2
Creo que la pregunta que realmente desea hacer es "¿Cómo hago que la configuración de mi aplicación esté fuertemente tipada?" Sin embargo, ha pasado mucho tiempo desde que trabajé con eso para escribir una respuesta adecuada.
Simon
Yah, idealmente no quiero tener que pasar el tipo al método. Solo voy a tener los 4 tipos que mencioné. Entonces, si se establece "verdadero" / "falso", quiero que esta función devuelva un valor booleano (sin necesidad de pasarlo al método), probablemente pueda combinar int y double en solo double, y todo lo demás debería ser una cadena. Lo que ya ha respondido funcionará bien, pero necesito pasar el tipo cada vez, lo que probablemente esté bien.
MacGyver
3
Su comentario parece que está pidiendo un método que devolverá un bool fuertemente tipado (o cadena, o int, o lo que sea) en tiempo de ejecución en función de los datos reales recuperados para la clave de nombre de configuración. C # no hará eso por ti; no hay forma de saber el tipo de ese valor en tiempo de compilación. En otras palabras, eso es escritura dinámica, no escritura estática. C # puede hacer eso por usted si usa la dynamicpalabra clave. Hay un costo de rendimiento para eso, pero para leer un archivo de configuración, el costo de rendimiento es casi ciertamente insignificante.
phoog

Respuestas:

354

Debe convertirlo en un método genérico, como este:

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

Pero la persona que llama tendrá que especificar el tipo que espera. A continuación, podría utilizar Convert.ChangeType, suponiendo que todos los tipos relevantes son compatibles:

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

No estoy completamente convencido de que todo esto sea una buena idea, eso sí ...

Jon Skeet
fuente
21
/ * código para convertir la configuración a T ... * / y aquí sigue toda la novela :)
Adrian Iftode
1
Si esto no fuera necesario, debe conocer el tipo de configuración que desea obtener, lo que puede no ser posible.
thecoshman
2
@ thecoshman: Lo haría, pero si no lo hicieras, ¿qué harías con el valor devuelto?
George Duckett
55
Mientras que esta respuesta es, por supuesto, correcta, y como síntesis responde a la petición de la OP, es probablemente vale la pena mencionar que el viejo enfoque de métodos independientes ( ConfigSettingString, ConfigSettingBool, etc.) tiene la ventaja de código de método que será más corta, más clara y mejor enfocado .
phoog
44
Si esto no se recomienda, ¿cuál es el propósito de los tipos de devolución genéricos?
bobbyalex
29

Podrías usar Convert.ChangeType():

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

Hay muchas formas de hacerlo (enumeradas por prioridad, específicas para el problema del OP)

  1. Opción 1: enfoque directo: cree varias funciones para cada tipo que espera en lugar de tener una función genérica.

    public static bool ConfigSettingInt(string settingName)
    {  
         return Convert.ToBoolean(ConfigurationManager.AppSettings[settingName]);
    }
    
  2. Opción 2: cuando no desee utilizar métodos sofisticados de conversión : convierta el valor en objeto y luego en tipo genérico.

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

    Nota: Esto arrojará un error si el lanzamiento no es válido (su caso). No recomendaría hacer esto si no está seguro sobre el tipo de conversión, prefiera la opción 3.

  3. Opción 3: Genérico con seguridad de tipo : cree una función genérica para manejar la conversión de tipo.

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

    Nota: T es el tipo esperado, tenga en cuenta la restricción where aquí (el tipo de U debe ser IConvertible para salvarnos de los errores)

RollerCosta
fuente
44
¿Por qué hacer que la tercera opción sea genérica U? No tiene sentido hacerlo, y hace que sea más difícil llamar al método. Solo acepta en su IConvertiblelugar. No creo que valga la pena incluir la segunda opción para esta pregunta dado que no responde a la pregunta que se hace. Probablemente también debería cambiar el nombre del método en la primera opción ...
Jon Skeet
7

Debe convertir el tipo de valor de retorno del método al tipo genérico que pasa al método durante la llamada.

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

Debe pasar un tipo que sea de tipo castable para el valor que devuelve a través de ese método.

Si desea devolver un valor que no es de tipo casteable al tipo genérico que pasa, es posible que deba modificar el código o asegurarse de pasar un tipo que sea casteable para el valor de retorno del método. Por lo tanto, este enfoque no se recomienda.

Vinay Chanumolu
fuente
Spot - para mí la última línea return (T)Convert.ChangeType(number, typeof(T));era exactamente lo que me faltaba - aplausos
Greg Trevellick
1

Cree una función y pase el parámetro put como de tipo genérico.

 public static T some_function<T>(T out_put_object /*declare as Output object*/)
    {
        return out_put_object;
    }
Prabhakar
fuente
En realidad, esto es bastante inteligente para algunos casos de uso. Como extraer datos de una base de datos. Usted sabe que obtendrá una lista de datos del tipo T. El método de carga simplemente no sabe qué tipo de T desea ahora. Así que simplemente pase una nueva Lista <ObjetoDeObjeto> a esto y el método puede hacer su trabajo y llenar la lista antes de devolverla. ¡Agradable!
Marco Heumann
0

Por favor, intente debajo del código:

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

Estaba realizando esto en todo mi código y quería una forma de ponerlo en un método. Quería compartir esto aquí porque no tuve que usar Convert.ChangeType para mi valor de retorno. Puede que esta no sea una buena práctica, pero funcionó para mí. Este método toma una matriz de tipo genérico y un valor para agregar al final de la matriz. La matriz se copia con el primer valor eliminado y el valor tomado en el método se agrega al final de la matriz. Lo último es que devuelvo la matriz genérica.

Adam Howard
fuente