C # obtiene el tipo de objeto nulo

Tengo el método C #

private static string TypeNameLower(object o) { return o.GetType().Name.ToLower(); } 

para darme el nombre del tipo de minúscula del objeto de entrada.

Pero si la entrada es una cadena establecida en nulo o un conjunto nulable en null, entonces este método, por supuesto, falla.

¿Cómo obtengo el nombre del tipo en esta situación?

Jeff está en lo cierto. Es como preguntar qué tipo de pastel habría estado en una caja vacía sin etiqueta.

Como alternativa a la respuesta de Fortran, también podrías:

 string TypeNameLower(T obj) { return typeof(T).Name.ToLower(CultureInfo.InvariantCulture); } string TypeNameLower(object obj) { if (obj != null) { return obj.GetType().Name.ToLower(CultureInfo.InvariantCulture); } else { return null; } } string s = null; TypeNameLower(s); // goes to the generic version 

De esta forma, C # seleccionará el genérico en tiempo de comstackción si conoce lo suficiente sobre el tipo que está pasando.

Pensé que publicaría mi respuesta, a pesar de que esta pregunta es antigua, porque en mi opinión, la respuesta aceptada es incorrecta. Esa respuesta fue bastante creativa, así que no es mi intención criticarla. Y, por lo que sé, podría ser lo que el OP realmente quería.

Pero, como verá en mis ejemplos a continuación, creo que en casi todos los casos, la idea de usar la función genérica descrita en la respuesta aceptada es (A) innecesaria o (B) totalmente incorrecta. Copié la función genérica a la que me refiero de la respuesta aceptada y la pegué a continuación como referencia:

 string TypeNameLower(T obj) { return typeof(T).Name.ToLower(); } 

Ahora, veamos algunas formas en que se puede usar esta función:

Ejemplos donde la función genérica es innecesaria:

 var s = "hello"; var t = TypeNameLower(s); //or foreach(char c in "banana") WriteLine(TypeNameLower(c)); //or foreach(MyCustomStruct x in listOfMyCustomStruct) WriteLine(TypeNameLower(x)); 

En estos ejemplos, la función funciona, es decir, devuelve los valores correctos que son “cadena”, “char” y “mycustomstruct”, respectivamente. Sin embargo, en todos los casos como estos (es decir, donde la función genérica realmente devuelve el tipo correcto), el comstackdor sabe de antemano cuál es el tipo definido de la variable, y también el progtwigdor, por supuesto (a menos que se confundan con sus nombres de variable). Entonces la función es completamente innecesaria, y el progtwigdor también pudo haber hecho esto:

 var s = "hello"; var t = "string"; //or foreach(char c in "banana") WriteLine("char"); //or foreach(MyCustomStruct x in listOfMyCustomStruct) WriteLine("mycustomstruct"); 

Eso puede parecer ingenuo al principio, pero piense en ello por un tiempo … puede tomar un tiempo para que realmente se hunda … Trate de llegar a CUALQUIER escenario donde el uso de la función genérica proporcione información precisa en Runtime que no esté Ya se sabe (y, por lo tanto, podría ser generado automáticamente por el comstackdor o las utilidades de generación de código, como plantillas T4) en tiempo de comstackción .

Ahora, el objective del conjunto de ejemplos anterior era simplemente demostrar una pequeña molestia con la función genérica, que es innecesario en todos los casos en que devuelve el resultado correcto . Pero más importante aún, eche un vistazo a los ejemplos a continuación. Demuestran que, en cualquier otro caso, el resultado de la función genérica es realmente incorrecto si espera que la función devuelva el nombre del verdadero tipo de tiempo de ejecución del objeto. La función solo está garantizada para devolver el nombre de un tipo al que se puede asignar el valor verdadero, que podría ser una clase ancestra, una interfaz u “objeto” en sí mismo.

Ejemplos donde la función genérica es incorrecta

 Stream ms = new MemoryStream(); IEnumerable str = "Hello"; IComparable i = 23; object j = 1; TypeNameLower(ms); //returns "stream" instead of "memorystream" TypeNameLower(str); //returns "ienumerable" instead of "string" TypeNameLower(i); //returns "icomparable" instead of "int32" TypeNameLower(j); //returns "object" instead of "int32" TypeNameLower(true); //returns "object" instead of "bool" 

En todos los casos, los resultados son bastante incorrectos como puede ver. Ahora, admito que las dos últimas líneas fueron un poco inventadas para demostrar el punto (sin mencionar que TypeNameLower(j) realidad sería comstackdo para usar la versión no genérica de la función que también es parte de la respuesta aceptada– Pero se entiende la idea…)

El problema es que la función en realidad ignora el tipo de objeto que se pasa y solo utiliza la información (en tiempo de comstackción) del tipo de parámetro genérico para devolver el valor.

Una mejor implementación sería la siguiente:

 string TypeNameLower(T obj) { Type t; if (obj == null) t = typeof(T); else t = obj.GetType(); return t.Name.ToLower(); } 

Ahora la función devuelve el nombre del tipo de tiempo de ejecución real siempre que el objeto no sea nulo, y se establece de manera predeterminada en el tipo de tiempo de comstackción / definido cuando el tipo es null .

¡Es importante destacar que esta función podría usarse SIN una versión no genérica! El resultado sería que la función nunca devolvería null . El valor de retorno más general sería “objeto”, por ejemplo:

  object x = null; string s = null; byte[] b = null; MyClass m = null; TypeNameLower(x); // returns "object" TypeNameLower(s); // returns "string" TypeNameLower(b); // returns "byte[]" TypeNameLower(m); // returns "myclass" 

Tenga en cuenta que esto es en realidad más coherente con el objective definido de la función, según lo solicitado por el OP. Es decir, si el OP realmente desea averiguar cuál era el nombre de tipo del objeto si no fuera nulo , devolver NULL NUNCA sería una respuesta adecuada, porque null NO ES el nombre de ningún Tipo, y typeof (null) no está definido.

Cada variable en C # desciende de System.Object , por lo que, por definición, si el valor no fuera null entonces sería un Object y en muchos casos eso es lo máximo que se puede determinar sobre una referencia nula en tiempo de ejecución.

 // Uses the compiler's type inference mechanisms for generics to find out the type // 'self' was declared with in the current scope. static public Type GetDeclaredType(TSelf self) { return typeof(TSelf); } void Main() { // ... Foo bar; bar = null; Type myType = GetDeclaredType(bar); Console.Write(myType.Name); } 

Huellas dactilares:

 Foo 

Publiqué esto también en un tema similar, espero que sea útil para ti. 😉

 if (o == null) return "null"; else return o.GetType().Name.ToLower(); 

solución simple para un problema simple :-p

Como otros mencionan, no puedes. En realidad, este es un problema bien conocido con idiomas que permiten referencias nulas a objetos. Una forma de evitarlo es usar el “Patrón de objeto nulo”. La idea básica es que, en lugar de usar null para referencias vacías, le asigne una instancia de un objeto “no hacer nada”. Por ejemplo:

 public class Circle { public virtual float Radius { get; set; } public Circle(float radius) { Radius = radius; } } public class NullCircle : Circle { public override float Radius { get { return float.NaN; } set { } } public NullCircle() { } } 

A continuación, puede pasar una instancia de NullCircle lugar de null y podrá probar su tipo como en su código.

Que yo sepa, no puedes. Nulo indica la ausencia de un valor y no es distinto para diferentes tipos.

No hay ninguna noción de que una cadena nula sea diferente de una matriz nula, sea diferente de una nula cualquier otra cosa. Desde el interior de su función, no puede determinar el nombre del tipo.

Más específicamente, una instancia de una clase de referencia (internamente) incluye un “puntero” al tipo de información sobre el objeto. Cuando la entrada es nula, no existe dicho puntero, por lo que la información del tipo no existe.

Simplemente expandiendo la respuesta de @Josh Einstein.

A continuación hay dos métodos de extensión para obtener el tipo de una variable, incluso si está configurada actualmente como nula.

  ///  /// Gets an object's type even if it is null. ///  /// The type of the object. /// The object being extended. /// The objects type. public static Type GetTheType(this T that) { return typeof(T); } ///  /// Gets an object's type even if it is null. ///  /// The object being extended. /// The objects type. public static Type GetTheType(this object that) { if (that != null) { return that.GetType(); } return null; } 

Además, aquí hay dos pruebas simples de unidades para probar los métodos de extensión.

  ///  /// Tests to make sure that the correct type is return. ///  [Test(Description = "Tests to make sure that the correct type is return.")] public void Test_GetTheType() { var value = string.Empty; var theType = value.GetTheType(); Assert.That(theType, Is.SameAs(typeof(string))); } ///  /// Tests to make sure that the correct type is returned even if the value is null. ///  [Test(Description = "Tests to make sure that the correct type is returned even if the value is null.")] public void Test_GetTheType_ReturnsTypeEvenIfValueIsNull() { string value = null; var theType = value.GetTheType(); Assert.That(theType, Is.SameAs(typeof(string))); } 

Editar Lo siento, olvidé mencionar que necesitaba esta misma característica exacta para un proyecto en el que estoy actualmente. Todo el crédito todavía debe ir a @Josh Einstein: D

Es muy frustrante que C # no permita que se haga tal determinación. Y no es similar a preguntar qué pastel tendría en una caja vacía, un objeto comprende dos componentes independientes: la “encarnación” del objeto y la información de la clase que se utilizó para crear el objeto. El hecho de que esta información no se puede acceder fácilmente es una omisión por parte de los desarrolladores de C #.

Todo lo que puedes hacer por determinación es este método bastante paralizante:

 void Method(object obj) { if(obj is int) { //obj is of the int type } else if(obj is SomeComplexType) { //obj is of the SomeComplexType type } } 

Por lo tanto, puede ver que incluso si el objeto es nulo, su información de tipo, sin embargo, viaja junto al objeto, no se pierde, simplemente no puede acceder a él. Pero esto es, por decir lo menos, inconveniente.

Si tiene un objeto por sí mismo (digamos como un parámetro de entrada a un método con tipo de objeto), sin definición o tipo genérico, no hay forma de encontrar el tipo. La razón es simple, no puede enviar un mensaje a (invocar cualquier método) sobre el objeto para preguntar sobre el tipo .

Podría haber algunas otras soluciones, como puede ver en algunas respuestas, como el uso de tipos generics. En ese caso, no está pidiendo el objeto nulo, está pidiendo el tipo genérico para su tipo.

Considera este código:

  public class MyClass1{} public class MyClass2{} public static void Test1() { MyClass1 one = null; MyClass2 two = (MyClass2) (object) one; one = new MyClass1(); //invalid cast exception two = (MyClass2)(object) one; } 

El tipo de tiempo de ejecución de una instancia nula es un object , al menos desde un punto de vista de seguridad del tipo.