C# 可空属性与可空局部变量
我对C# 可空属性与可空局部变量,c#,reflection,nullable,C#,Reflection,Nullable,我对Nullable类型的以下行为感到困惑: class TestClass { public int? value = 0; } TestClass test = new TestClass(); 现在,Nullable.getunderyingtype(test.value)返回底层Nullable类型,即int。 但是,如果我试图获得这样的字段类型 FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instan
Nullable
类型的以下行为感到困惑:
class TestClass {
public int? value = 0;
}
TestClass test = new TestClass();
现在,Nullable.getunderyingtype(test.value)
返回底层Nullable
类型,即int
。
但是,如果我试图获得这样的字段类型
FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];
我调用
Nullable.GetUnderlyingType(field.FieldType).ToString()
它返回一个
System.Nullable[System.Int32]
类型。因此,这意味着方法Nullable.GetUnderlyingType()
具有不同的行为,具体取决于您获取成员类型的方式。为什么会这样?如果我简单地使用test.value
如何在不使用反射的情况下判断它是可空的问题在于,您假设test.value
的类型与字段的类型相同,而value
的字段类型与字段类型相同。获取测试的类型
。值
实际上获取字段中存储内容的类型
,在本例中为0
Type t=test.value.GetType()
(在您的示例中)与
Type t=0.GetType()
为了演示,将value
初始化为null,并test.value.GetType()
将抛出NullReferenceException
可为null的类型有点奇怪。然而,至少他们的行为是有据可查的
从MSDN的C#编程指南中,
“如何:标识可为空的类型”地址:
您还可以使用System.Reflection命名空间的类和方法来生成表示可空类型的类型对象。但是,如果尝试在运行时使用GetType方法或is运算符从可空变量获取类型信息,则结果是一个表示基础类型的类型对象,而不是可空类型本身。
对可为null的类型调用GetType会导致在该类型隐式转换为Object时执行装箱操作。因此,GetType总是返回一个表示底层类型的类型对象,而不是可为null的类型。
值得指出的是,你的标题中的区别是不准确的。类型行为并不是基于局部变量或属性的,而是基于是通过运行时对象还是通过反射(或使用typeof运算符)访问类型。您的推断是可以理解的,因为局部变量的类型通常只能通过运行时对象进行访问,但是,这是有缺陷的,因为如果您在运行时通过属性访问器访问可为null的对象,则其行为将与局部变量的行为等效
另外,要明确回答问题的最后一部分:告诉test.value可以为null而不使用反射的唯一方法是访问它并获取一个NullReferenceException(当然,这只能在test.value为null时发生。如前所述,在您的示例中,该值不是null,因此在没有反射的情况下确定该值是不可能的方法GetNullableUnderlyingType为可为null的类型返回基础类型,为其他类型返回null
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetNulllableUnderlyingType(new TestClass().value));
Console.WriteLine(GetNulllableUnderlyingType(new TestClass()));
Console.ReadKey();
}
public class TestClass
{
public int? value;
}
public static Type GetNulllableUnderlyingType<T>(T value)
{
Type result = Nullable.GetUnderlyingType(typeof (T));
return result;
}
}
类程序
{
静态void Main(字符串[]参数)
{
WriteLine(GetNulllableUnderlyingType(newTestClass().value));
WriteLine(GetNulllableUnderlyingType(newTestClass());
Console.ReadKey();
}
公共类TestClass
{
公共价值;
}
公共静态类型GetNulllableUnderlyingType(T值)
{
Type result=Nullable.getUnderlineType(typeof(T));
返回结果;
}
}
首先,示例中的null.getunderyingtype(test.value)
不会编译。或者typeof(test.value)
正如我在注释中看到的那样
让我们通过以下修改的代码来回答这个问题:
TestClass test = new TestClass();
Type type1 = test.value.GetType(); // Gets the type of the data inside the field ==> System.Int32.
Type underlyingType1 = Nullable.GetUnderlyingType(type1); // Gets the underlying type of System.Int32 ==> null.
FieldInfo field = typeof(TestClass).GetFields(BindingFlags.Instance | BindingFlags.Public)[0];
Type type2 = field.FieldType; // Now the type is retrieved of the field, not the data inside. ==> System.Int32?
Type underlyingType2 = Nullable.GetUnderlyingType(type2); // Gets the underlying type of System.Int32? ==> System.Int32.
当您对字段执行GetType()
操作时,您将获得其中的数据类型,而不是字段本身的类型。例如:
object o = 1;
Type type = o.GetType();
在本例中,当调用GetType()时
,您还将得到System.Int32
,而不是System.Object
。因为在调用GetUnderylingType
之前,您首先使用不同的类型,最终得到的结果不同。smartcaveman的答案是迄今为止最好的答案,因为它实际上确定了文档中描述他对这种行为表示不满
这种行为是不受欢迎和不幸的;这是由于这种行为结合了三个特征,而这三个特征本身就表现得很合理
这三个特点是:
GetType
是一个非虚拟的方法;它不能被重写。这应该是有意义的;对象不能决定它的报告类型。通过使其非虚拟,该方法可以保证说出真相
- 传递给
对象
上声明的非虚方法的此
值必须转换为对象
;因此,对于值类型的对象,调用GetType()
的接收者被装箱到对象
- 可为null的值类型没有装箱形式;当您装箱可为null的int时,您要么得到装箱的int,要么得到null引用。您永远不会得到对装箱可为null的int的有效引用
每个特性本身都是合理的,但结合起来,结果是不可取的:当您对有效的可空int调用GetType
时,运行时将可空int装箱为一个已装箱的int,然后将其作为对象的this
传递。GetType
当然会报告int
。如果该值是可空的int时,运行时框设置为null,然后对null引用调用GetType
,然后崩溃。这两种行为都不可取,但我们都被它们卡住了。是typeof(field.FieldType)==typeof(test.value)
?我可以想象,因为该方法是ca