2008-11-19 21 views
418

Tengo un método genérico con este código (ficticio) (sí, soy consciente de que IList tiene predicados, pero mi código no usa IList sino otra colección, de todos modos esto es irrelevante para la pregunta ...)¿Cómo puedo devolver NULL desde un método genérico en C#?

static T FindThing<T>(IList collection, int id) where T : IThing, new() 
{ 
    foreach T thing in collecion 
    { 
     if (thing.Id == id) 
      return thing; 
    } 
    return null; // ERROR: Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead. 
} 

Esto me da un error de generación

"no se puede convertir nulo al tipo de parámetro 'T', porque podría ser un tipo de valor. considerar el uso de 'default (T)' en su lugar."

¿Se puede evitar este error?

+1

¿Por qué esta wiki de la comunidad? – vitule

+2

Porque pensé que esto hace que esta comunidad de preguntas sea editable? – edosoft

Respuesta

741

Dos opciones:

  • retorno default(T) lo que significa que regrese null si T es un tipo de referencia (o un tipo de valor anulable), 0 para int, '\ 0' para el carbón, etc
  • restringir T para ser un tipo de referencia con la restricción where T : class y luego devolver null como normales
+3

¿Qué pasa si mi tipo de devolución es una enumeración no una clase? No puedo especificar T: enum :( – Justin

+1

En .NET una enumeración es una envoltura muy delgada (y bastante permeable) alrededor de un tipo entero. La convención es usar cero para su valor de enum "predeterminado". –

+18

Creo que el problema con esto es que si está utilizando este método genérico para decir, convierta un objeto de base de datos de DbNull a Int y devuelve el valor predeterminado (T) donde T es un int, devolverá 0. Si este número es realmente significativo, entonces estarías pasando datos incorrectos en los casos en que ese campo era nulo. O un mejor ejemplo sería un DateTime. Si el campo era algo así como "DateClosed" y se devolvió como nulo porque y la cuenta todavía está abierta, en realidad sería predeterminado (DateTime) a 1/1/0000, lo que implica que la cuenta se cerró antes de que se inventaran las computadoras. – Sinaesthetic

61
return default(T); 
+0

Este enlace: http://msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspx debe explicar por qué. –

+0

Maldita sea, habría ahorrado mucho tiempo si hubiera sabido sobre esta palabra clave, ¡gracias Ricardo! –

+0

Me sorprende que esto no haya obtenido más votos ya que la palabra clave 'predeterminada' es una solución más completa, que permite el uso de tipos que no sean de referencia junto con los tipos y estructuras numéricos. Si bien la respuesta aceptada resuelve el problema (y de hecho es útil), es mejor que responda cómo restringir el tipo de devolución a los tipos anulables/de referencia. –

1

Tome la recomendación del error ... y ya sea de usuario o default(T)new T.

Deberá agregar una comparación en el código para asegurarse de que sea una coincidencia válida si toma esa ruta.

De lo contrario, considere potencialmente un parámetro de salida para "coincidencia encontrada".

5

su otra opción sería la de añadir esto al final de su declaración:

where T : class 
    where T: IList 

De esta manera le permitirá devolver nulo.

22

Sólo puede ajustar sus limitaciones: se permite

where T : class, IDisposable 

nula luego regresar.

+0

Gracias. No puedo elegir 2 respuestas como la solución aceptada, entonces elijo la causa de John Skeet porque su respuesta tiene dos soluciones. – edosoft

+5

Ligeramente mal, no tiene que ser IDisposable ... – Migol

10

Agregue la restricción de clase como la primera restricción a su tipo genérico. solución

static T FindThing<T>(IList collection, int id) where T : class, IThing, new() 
+0

Gracias. No puedo elegir 2 respuestas como la solución aceptada, entonces elijo la causa de John Skeet porque su respuesta tiene dos soluciones. – edosoft

1

de TheSoftwareJedi funciona,

también puede archivar con el uso de dos tipos de valor y anulables:

static T? FindThing<T>(IList collection, int id) where T : struct, IThing 
{ 
    foreach T thing in collecion 
    { 
     if (thing.Id == id) 
      return thing; 
    } 
    return null; 
} 
5
  1. Si tiene objetos continuación, tendrá que encasillado

    return (T)(object)(employee); 
    
  2. si necesita devolver nulo.

    return default(T); 
    
+0

Hola, usuario725388, verifique la primera opción –

1

Aquí está un ejemplo de trabajo para los valores de retorno anulable Enum:

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct 
{ 
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value); 
} 
1

A continuación se presentan la opción de dos puede utilizar

return default(T); 

o

where T : class, IDisposable 
return null; 
Cuestiones relacionadas