我有C#方法
private static string TypeNameLower(object o)
{
return o.GetType().Name.ToLower();
}
给我一个输入对象的小写类型名称。
但是如果input是一个设置为null的字符串或者设置为null的nullable int,那么这个方法当然会失败。
在这种情况下如何获取类型名称?
答案 0 :(得分:64)
杰夫是对的。这就像问一个没有标签的空盒子里会有什么样的蛋糕。
作为Fortran答案的替代方案,您也可以这样做:
string TypeNameLower<T>(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
这样,如果C#对你传入的类型有足够的了解,它将在编译时选择泛型。
答案 1 :(得分:34)
我以为我会发布我的答案,即使这个问题已经过时了,因为在我看来,接受的答案是错误的。这个答案很有创意,所以我不是故意要敲门。而就我所知,可以成为OP真正想要的。
但是,正如您将从下面的示例中看到的那样,我认为在几乎所有的情况下,使用接受答案中描述的通用函数的想法是(A)不必要或( B)完全错误。 我已经从接受的答案中复制了我正在谈论的通用函数,并将其粘贴在下面作为参考:
string TypeNameLower<T>(T obj) {
return typeof(T).Name.ToLower();
}
现在,让我们看看可以使用此功能的一些方法:
通用功能不必要的示例:
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”。但是在所有这些情况下(即泛型函数确实返回正确的类型),编译器提前知道变量的定义类型是什么,当然程序员也是如此(除非他们对此感到困惑)他们的变量名称)。所以函数是完全没必要的,程序员也可以这样做:
var s = "hello";
var t = "string";
//or
foreach(char c in "banana")
WriteLine("char");
//or
foreach(MyCustomStruct x in listOfMyCustomStruct)
WriteLine("mycustomstruct");
一开始可能看起来很天真,但想一想......它可能需要一段时间才能真正沉入其中......试着想出任何使用泛型函数提供准确信息的场景运行时在编译时时尚未知(因此可由编译器或代码生成实用程序(如T4模板)自动生成)。
现在,上一组示例的目的只是为了演示泛型函数的一个小麻烦 - 在每种情况下它都不需要返回正确的结果。但更重要的是,请看下面的示例。他们演示了在任何其他情况下,如果您希望函数返回 true 的名称,则泛型函数的结果实际上是 错误 对象的运行时类型。该函数实际上仅 保证返回真值可分配的类型的名称,该类型可能是祖先类,接口或“对象”本身。
通用功能错误的示例
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"
在所有情况下,如您所见,结果非常错误。现在,我承认最后两行有点用来证明这一点(更不用说TypeNameLower(j)
实际上会被编译为使用非通用版本的函数,这也是公认答案的一部分 - - 但你明白了......)
问题是该函数实际上忽略了传入的对象的类型,并且只使用泛型参数类型的(编译时)信息来返回值。
更好的实施方案如下:
string TypeNameLower<T>(T obj) {
Type t;
if (obj == null)
t = typeof(T);
else
t = obj.GetType();
return t.Name.ToLower();
}
现在,只要对象为非null,函数就会返回真实运行时类型的名称,当类型为null
时,它默认为编译时/定义类型。
重要的是,此功能可以在没有非通用版本的情况下使用!!
结果是该函数将从不返回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的要求,这实际上与函数的定义目标更一致。也就是说,如果OP 确实确实想要找出对象的类型名称,如果它不是空,那么返回null将永远不会是合适的回答,因为null不是任何Type的名称,并且未定义typeof(null)。
C#中的每个变量都来自System.Object
,因此根据定义,如果值不是null
,那么将成为Object
,那就是在许多情况下,可以在运行时确定关于空引用的最多。
答案 2 :(得分:18)
// 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
我也在一个类似的主题上发布了这个,我希望它对你有用。 ; - )
答案 3 :(得分:15)
if (o == null) return "null";
else return o.GetType().Name.ToLower();
解决一个简单问题的简单方法:-p
答案 4 :(得分:8)
正如其他人所说,你不能。对于允许对对象进行纯空引用的语言,这实际上是一个众所周知的问题。解决它的一种方法是使用“空对象模式”。基本思想是,不是将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
的实例,并且您将能够像在代码中一样测试其类型。
答案 5 :(得分:3)
据我所知,你做不到。 Null表示没有值,并且对于不同的类型没有区别。
答案 6 :(得分:2)
没有任何概念认为空字符串不同于null数组不同于null其他任何东西。从函数内部,您无法确定类型名称。
更具体地说,引用类的实例(内部)包括指向关于对象的类型信息的“指针”。当输入为null时,没有这样的指针,因此类型信息不存在。
答案 7 :(得分:2)
只是扩展@Josh爱因斯坦的答案。
以下是获取变量类型的两种扩展方法,即使它当前设置为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;
}
此外,这里有两个简单的单元测试来测试扩展方法。
/// <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)));
}
修改强> 的 对不起,我忘了提到我目前正在进行的项目需要这个完全相同的功能。所有的功劳仍然归于@Josh Einstein:D
答案 8 :(得分:1)
令人非常沮丧的是,C#不允许做出这样的决定。并且它不等于询问你在一个空盒子里会有什么蛋糕 - 一个对象包含两个独立的组件 - 对象的“化身”和用于创建对象的类的信息。无法轻易访问此信息这一事实是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,它的类型信息仍然沿着对象传播,它不会丢失,你只是不能轻易地访问它。 但至少可以说这是不方便的。
答案 9 :(得分:1)
如果你自己有一个对象(比方说作为带有类型对象的方法的输入参数),没有定义或泛型类型,则无法找到该类型。原因很简单,你不能发送消息(调用任何方法)对象询问类型。
正如您在某些答案中看到的那样,可能还有其他一些变通方法,例如使用泛型类型。在这种情况下,您不是在询问Null对象,而是询问泛型类型的类型。
答案 10 :(得分:0)
考虑以下代码:
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;
}
空实例的运行时类型为object
,至少从类型安全的角度来看。