2012-05-02 15 views
6

Quiero una función de ayuda de búsqueda tipada para una colección heterogénea: debe devolver una estructura o clase, de lo contrario nula si no se encuentra el elemento. A continuación se muestra un ejemplo con una búsqueda trivial de colecciones, pero podría ser una llamada a la base de datos o lo que sea.¿Cómo implementar el método de búsqueda de colecciones genéricas que puede devolver la clase o estructura anulable?

¿Hay alguna manera de lograr esto con una única firma de método?

public T GetClass<T>(string key) where T : class 
    { 
     object o; 
     if (Contents.TryGetValue(key, out o)) 
     { 
      return o as T; 
     } 
     return null; 
    } 

    public T? GetStruct<T>(string key) where T : struct 
    { 
     object o; 
     if (Contents.TryGetValue(key, out o)) 
     { 
      if (o is T) 
      { 
       return (T?) o; 
      } 
     } 
     return null; 
    } 

lo que ya he intentado:

  • entiendo que las restricciones genéricas no pueden ser utilizados para eliminar la ambigüedad sobrecargas. Entonces no puedo simplemente darles el mismo nombre a estos dos métodos.
  • La devolución (Default) T no es una opción, ya que 0 es un valor int válido.
  • He intentado llamar con <int ?> como el tipo, pero como se discutió, Nullable<T> no es un tipo de referencia.

¿Hay alguna manera de indicar que voy a devolver un int encuadrado?

Respuesta

3

El siguiente método funciona para ambas clases y estructuras anulables:

public static T GetValue<T>(string key) 
{ 
    object o; 
    if (Contents.TryGetValue(key, out o)) 
    { 
     if (o is T) 
     { 
      return (T)o; 
     } 
    } 
    return default(T); 
} 

Uso:

int? result1 = GetValue<int?>("someInt"); 
string result2 = GetValue<string>("someString"); 

Aviso cómo el ? es parte del argumento de tipo genérico y no se definen por el método de el tipo de devolución.

+3

que la mitad de este tipo - pero * permite * llamar a él como 'getClass ' y devuelve 0 para " encontrado con valor 0 "y" no encontrado "indistinguiblemente. De hecho, podrías perder accidentalmente el segundo '?' En tu uso de muestra y obtener un error bastante sutil. ('result1' would * never * be null, pero es de esperar que lo sea) –

+0

Creo que el valor predeterminado para int? es nulo – akatakritos

+0

Pero, ¿qué pasa si el valor que está buscando es un int regular: creo que (o es int?) ¿fallará bc int? no es un int. – akatakritos

6

¿Hay alguna manera de lograr esto con una única firma de método?

Hay una manera horrible (verdaderamente horrible) para hacerlo usando parámetros opcionales para que el código de llamada puede parecer el mismo en ambos casos. Aunque es asqueroso.

Opciones:

  • devolver un Tuple<T, bool> en lugar de utilizar la nulidad
  • Usar un parámetro out (como int.TryParse etc)
  • utilizar diferentes nombres de los métodos

Tenga en cuenta que al señalar la ausencia de un valor por separado, puede hacer que null sea un resultado "encontrado" válido, que a veces puede ser útil. O bien, puede querer simplemente garantizar que nunca será devuelto.

Si realmente desea utilizar la nulidad, me gustaría ir a la última opción. Creo que hará que tu código sea más claro de todos modos. IMO, la sobrecarga solo debería usarse cuando los métodos son exactamente lo mismo que se expresa con diferentes parámetros - mientras que devolver Nullable<T> como el tipo de devolución en un caso y T como el tipo de devolución en el otro caso realmente no se puede ver camino.

1

Esto debería hacer exactamente lo que usted necesita. Si el tipo solicitado es de tipo anulable, verifique el tipo subyacente antes de la conversión.

public static T GetValue<T>(string key) 
{ 
    object o; 
    if (Contents.TryGetValue(key, out o)) 
    { 
     if (o is T || Nullable.GetUnderlyingType(typeof(T)) == o.GetType()) 
     { 
      return (T)o; 
     } 
    } 

    return default(T); 
} 

Mi código de prueba:

Contents.Add("a string", "string value"); 
Contents.Add("an integer", 1); 
Contents.Add("a nullable integer", new Nullable<int>(2)); 

// Get objects as the type we originally used. 
Debug.WriteLine(string.Format("GetValue<string>(\"a string\") = {0}", GetValue<string>("a string"))); 
Debug.WriteLine(string.Format("GetValue<int>(\"an integer\") = {0}", GetValue<int>("an integer"))); 
Debug.WriteLine(string.Format("GetValue<int?>(\"a nullable integer\") = {0}", GetValue<int?>("a nullable integer"))); 

// Get objects as base class object. 
Debug.WriteLine(string.Format("GetValue<object>(\"a string\") = {0}", GetValue<object>("a string"))); 
Debug.WriteLine(string.Format("GetValue<object>(\"an integer\") = {0}", GetValue<object>("an integer"))); 
Debug.WriteLine(string.Format("GetValue<object>(\"a nullable integer\") = {0}", GetValue<object>("a nullable integer"))); 

// Get the ints as the other type. 
Debug.WriteLine(string.Format("GetValue<int?>(\"an integer\") = {0}", GetValue<int?>("an integer"))); 
Debug.WriteLine(string.Format("GetValue<int>(\"a nullable integer\") = {0}", GetValue<int>("a nullable integer"))); 

// Attempt to get as a struct that it's not, should return default value. 
Debug.WriteLine(string.Format("GetValue<double>(\"a string\") = {0}", GetValue<double>("a string"))); 

// Attempt to get as a nullable struct that it's not, or as a class that it's not, should return null. 
Debug.WriteLine(string.Format("GetValue<double?>(\"a string\") = {0}", GetValue<double?>("a string"))); 
Debug.WriteLine(string.Format("GetValue<StringBuilder>(\"a string\") = {0}", GetValue<StringBuilder>("a string"))); 

Resultados:

GetValue<string>("a string") = string value 
GetValue<int>("an integer") = 1 
GetValue<int?>("a nullable integer") = 2 

GetValue<object>("a string") = string value 
GetValue<object>("an integer") = 1 
GetValue<object>("a nullable integer") = 2 

GetValue<int?>("an integer") = 1 
GetValue<int>("a nullable integer") = 2 

GetValue<double>("a string") = 0 

GetValue<double?>("a string") = 
GetValue<StringBuilder>("a string") = 
+0

'(object) 42 is int?' Is true. No hay necesidad de '|| Nullable ... 'verificar. – dtb

+0

Dang it, a veces odio la magia del compilador alrededor de anulables. Ninguna otra estructura devolvería verdadera para ' es '. –

Cuestiones relacionadas