C#获取null对象的types

我有C#方法

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

给我input对象的小写types名称。

但是,如果input是一个string设置为null或可空的int设置为null,那么这个方法当然失败。

在这种情况下如何获得types名称?

杰夫是正确的。 这就像问一个没有标签的空盒子里会有什么样的蛋糕。

作为Fortran答案的替代方法,您也可以这样做:

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

这样,C#将在编译时select通用的,如果它足够了解你传入的types。

我以为我会回答我的答案,即使这个问题是旧的,因为在我看来,接受的答案是错误的。 这个答案很有创意,所以我不打算敲它。 就我所知,这可能是OP真正想要的。

但是,从下面的例子中可以看出,我认为在几乎所有情况下,使用被接受的答案中描述的通用函数的想法是(A)不必要的或者(B)错误的。 我已经从接受的答案中复制了我正在谈论的generics函数,并将其粘贴在下面以供参考:

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

现在,让我们看看可能使用这个函数的一些方法:

通用function不必要的例子:

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

在这些例子中,这个函数起作用 – 也就是说,它返回正确的值,分别是“string”,“char”和“mycustomstruct”。 然而,在所有这些情况下(即通用函数实际返回正确types的地方),编译器提前知道variables的定义types,程序员当然也知道(除非他们感到困惑他们的variables名称)。 所以这个函数是完全没有必要的,程序员也可以这样做:

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

这可能起初看起来很幼稚,但是想一下它可能需要一段时间才能真正沉入其中…试图想出使用通用函数在运行时提供准确信息的任何场景(因此可以在编译时由编译器或代码生成工具自动生成,例如T4模板)。

现在,前面一组示例的目的仅仅是为了展示一个通用函数的轻微烦恼 – 在任何情况下都不需要它返回正确的结果。 但更重要的是,请看下面的例子。 他们演示了在任何其他情况下,如果希望函数返回对象的真正运行时types的名称,则generics函数的结果实际上是错误的。 该函数实际上保证返回可赋值为true的types的名称,该types可能是祖先类,接口或“对象”本身。

generics函数错误的示例

 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<object>(true); //returns "object" instead of "bool" 

在所有情况下,结果都是错误的,你可以看到。 现在,我承认最后两行有一点点设法来certificate这一点(更不用说TypeNameLower(j)实际上将被编译为使用函数的非generics版本,这也是接受的答案的一部分 -但你明白了…)

问题是函数实际上忽略了被传入的对象的types,只使用通用参数types的(编译时)信息返回值。

更好的实施如下:

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

现在,只要对象为非空,该函数就会返回真正的运行时types的名称,当types为空时,它将默认为编译时/定义types。

重要的是,这个function可以使用,而不是一个非通用的版本! 结果将是该函数永远不会返回null 。 最普遍的返回值将是“对象”,例如:

  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" 

请注意,这实际上与OP所要求的function的定义目标更为一致 。 也就是说,如果OP 真的想知道对象的types名称是否为null ,那么返回null将永远不会是一个合适的答案,因为null不是任何types的名称, typeof(null)没有被定义。

C#中的每个variables都是从System.Object下来的,所以根据定义,如果这个值不是null那么它就是一个Object ,在很多情况下,它们是在运行时可以确定的一个空引用。

 // 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>(TSelf self) { return typeof(TSelf); } void Main() { // ... Foo bar; bar = null; Type myType = GetDeclaredType(bar); Console.Write(myType.Name); } 

打印:

 Foo 

我也发表了类似的话题,我希望这对你有任何用处。 😉

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

简单的解决scheme,一个简单的问题: – P

正如其他人所说,你不能。 这实际上是一个众所周知的问题,允许纯引用空对象的语言。 解决此问题的一种方法是使用“空对象模式”。 基本的想法是,而不是为空引用使用null ,你分配给它一个“什么都不做”的对象的实例。 例如:

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

然后,您可以传递NullCircle的实例而不是null ,您将能够像在代码中一样testing它的types。

据我所知,你不能。 Null表示缺less一个值,对于不同的types不是不同的。

没有一个概念,一个空string不同于一个空数组是不同于一个空任何其他。 从你的函数内部,你不能确定types的名称。

更具体地说,参考类(内部)的实例包括关于对象的types信息的“指针”。 当input为空时,不存在这样的指针,所以types信息不存在。

只是扩展@Jos​​h爱因斯坦的答案。

下面是两个扩展方法来获取variables的types,即使它当前设置为null。

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

另外,这里有两个简单的unit testing来testing扩展方法。

  /// <summary> /// Tests to make sure that the correct type is return. /// </summary> [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))); } /// <summary> /// Tests to make sure that the correct type is returned even if the value is null. /// </summary> [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))); } 

编辑对不起,我忘了提及我需要这个完全相同的function,我正在进行一个项目。 所有的信用仍然应该去@Josh Einstein:D

C#不允许做出这样的决定是非常令人沮丧的。 这与在一个空盒子里问什么蛋糕不同 – 一个对象包含两个独立的组件 – 对象的“化身”和用于创build对象的类的信息。 这个信息不能被轻易访问的事实是C#开发者的一个遗漏。

所有你可以通过确定的方式做的是这个相当残酷的方法:

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

所以,即使对象是null,它的types信息也是在对象的旁边旅行,它不会丢失,你只是不能方便地访问它。 但是,这至less是不方便的。

如果你自己拥有一个对象(比方说一个带有对象types的方法的input参数),没有定义或genericstypes,那么就没有办法find这个types。 原因很简单, 你不能发送消息到(调用任何方法)对象来询问types

可以有一些其他的解决方法,就像你在一些答案中看到的一样,比如使用genericstypes。 在这种情况下,你不问Null对象,你问genericstypes的types。

考虑这个代码:

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

至less从types安全的angular度来看,空实例的运行时types是object