C # obtém o tipo de object nulo

Eu tenho o método C #

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

para me dar o nome do tipo de letra minúscula do object de input.

Mas se a input for uma string configurada como null ou um int anulável configurado como null, esse método falhará.

Como obtenho o nome do tipo nesta situação?

Jeff está correto. É como perguntar que tipo de bolo teria sido em uma checkbox vazia sem label.

Como alternativa à resposta do Fortran, você também pode fazer:

 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 

Dessa forma, o C # escolherá o genérico em tempo de compilation, se souber o suficiente sobre o tipo em que você está passando.

Pensei em postar minha resposta, mesmo que essa pergunta seja antiga, porque, na minha opinião, a resposta aceita está errada. Essa resposta foi muito criativa, então eu não quero bater nela. E pelo que sei, pode ser o que o OP realmente queria.

Mas, como você verá nos exemplos abaixo, acho que, em quase todos os casos, a idéia de usar a function genérica descrita na resposta aceita é (A) desnecessária ou (B) incorreta. Eu copiei a function genérica da qual estou falando e colei-a abaixo para referência:

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

Agora, vamos ver algumas maneiras pelas quais essa function pode ser usada:

Exemplos em que a function genérica é desnecessária:

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

Nestes exemplos, a function funciona – ou seja, ele retorna os valores corretos que são “string”, “char” e “mycustomstruct”, respectivamente. No entanto, em todos os casos como esses (isto é, onde a function genérica realmente retorna o tipo correto), o compilador sabe antecipadamente qual é o tipo definido da variável, e o programador também, é claro (a menos que se confundam seus nomes de variables). Então a function é completamente desnecessária, e o programador pode ter feito isso:

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

Isso pode parecer ingênuo no começo, mas pense nisso por um tempo … pode demorar um pouco até que ele realmente afunde … Tente pensar em QUALQUER cenário em que usar a function genérica fornece informações precisas em Tempo de Execução que não são já conhecido (e, portanto, pode ser gerado automaticamente pelo compilador ou por utilitários de geração de código, como modelos T4) em tempo de compilation .

Agora, o ponto do conjunto anterior de exemplos era apenas demonstrar um pequeno incômodo com a function genérica – que é desnecessário em todos os casos em que ele retorna o resultado correto . Mas mais importante, dê uma olhada nos exemplos abaixo. Eles demonstram que, em qualquer outro caso, o resultado da function genérica é realmente errado se você espera que a function retorne o nome do verdadeiro tipo de tempo de execução do object. Na verdade, a function só tem a garantia de retornar o nome de um tipo para o qual o valor verdadeiro é designável, que pode ser uma class ancestral, uma interface ou um “object” em si.

Exemplos em que a function genérica está errada

 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" 

Em todos os casos, os resultados são muito errados, como você pode ver. Agora, admito que as duas últimas linhas foram um pouco planejadas para demonstrar o ponto (sem mencionar que TypeNameLower(j) seria realmente compilado para usar a versão não genérica da function que também faz parte da resposta aceita. mas você tem a ideia …)

O problema é que a function na verdade ignora o tipo de object que está sendo passado e usa somente as informações (tempo de compilation) do tipo de parâmetro genérico para retornar o valor.

Uma implementação melhor seria a seguinte:

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

Agora, a function retorna o nome do verdadeiro tipo de tempo de execução sempre que o object é não-nulo, e o padrão é o tipo de tempo de compilation / definido quando o tipo é null .

É importante ressaltar que essa function pode ser usada SEM uma versão não genérica !! O resultado seria que a function nunca retornaria null . O valor de retorno mais geral seria “object”, por exemplo:

  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" 

Observe que isso é realmente mais consistente com o objective definido da function, conforme solicitado pelo OP. Isto é, se o OP realmente quer descobrir qual era o nome do tipo do object se ele não fosse null , então retornar null NUNCA seria uma resposta apropriada, porque null não é o nome de nenhum tipo, e typeof (null) não está definido.

Todas as variables ​​em C # são descendentes de System.Object , portanto, por definição, se o valor não for null então seria um Object e, em muitos casos, é o máximo que pode ser determinado sobre uma referência nula no tempo de execução.

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

Impressões:

 Foo 

Eu postei isso também em um tópico semelhante, espero que seja de alguma utilidade para você. 😉

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

solução simples para um problema simples:

Como os outros mencionam, você não pode. Este é realmente um problema bem conhecido com linguagens que permitem referências nulas a objects. Uma maneira de contornar isso é usar o “padrão de object nulo”. A idéia básica é que, em vez de usar null para referências vazias, você atribui a ele uma instância de um object “do nothing”. Por exemplo:

 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() { } } 

Você pode então passar uma instância de NullCircle vez de null e você poderá testar seu tipo como em seu código.

Para o melhor do meu conhecimento você não pode. Nulo indica a ausência de um valor e não é distinto para tipos diferentes.

Não há noção de que uma cadeia nula é diferente de uma matriz nula é diferente de qualquer outra coisa nula. De dentro da sua function, você não pode determinar o nome do tipo.

Mais especificamente, uma instância de uma class de referência (internamente) inclui um “ponteiro” para as informações de tipo sobre o object. Quando a input é nula, não existe esse ponteiro, portanto, as informações de tipo não existem.

Apenas expandindo a resposta do @Josh Einstein.

Abaixo estão dois methods de extensão para obter o tipo de uma variável, mesmo se ela estiver atualmente definida 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; } 

Além disso, aqui estão dois testes de unidade simples para testar os methods de extensão.

  ///  /// 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 Desculpe, esqueci de mencionar que estava precisando exatamente desse mesmo recurso para um projeto no qual estou atualmente. Todo o crédito ainda deve ir para @Josh Einstein: D

É muito frustrante que o C # não permita que tal determinação seja feita. E não é como perguntar qual bolo você teria em uma checkbox vazia – um object compreende dois componentes independentes – a “encarnação” do object e as informações sobre a class que foi usada para criar o object. O fato de que esta informação não pode ser acessada facilmente é uma omissão por parte dos desenvolvedores do C #.

Tudo o que você pode fazer por meio de determinação é este método bastante incapacitante:

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

Assim, você pode ver que, mesmo que o object seja nulo, suas informações de tipo estão viajando ao lado do object, elas não são perdidas, você simplesmente não pode acessá-las com facilidade. Mas isso é, no mínimo, inconveniente.

Se você tiver um object por si só (digamos como um parâmetro de input para um método com object de tipo), sem definição ou tipo genérico, não é possível encontrar o tipo. A razão é simples, você não pode enviar uma mensagem para (invocar qualquer método sobre) o object para perguntar sobre o tipo .

Pode haver algumas outras soluções alternativas, como você vê em algumas respostas, como usar tipos genéricos. Nesse caso, você não está pedindo o object Nulo, você está pedindo o tipo genérico para o seu tipo.

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

O tipo de tempo de execução de uma instância nula é object , pelo menos do ponto de vista de segurança de tipo.