可空类型不是可空类型吗?

时间:2022-03-12 22:27:51

I was doing some testing with nullable types, and it didn't work quite as I expected:

我用可空类型做了一些测试,结果并没有达到我的预期:

int? testInt = 0;
Type nullableType = typeof(int?);
Assert.AreEqual(nullableType, testInt.GetType()); // not the same type

This doesn't work either:

这并不工作:

DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable)); //FAIL 

DateTime? test = new DateTime(434523452345);
Assert.IsTrue(test.GetType() == typeof(Nullable<>)); //STILL FAIL

My question is why does testInt.GetType() return int, and typeof(int?) return the true nullable type?

我的问题是,为什么testInt.GetType()返回int类型和typeof(int?)返回真正的可空类型?

4 个解决方案

#1


53  

According to the MSDN :

根据MSDN:

Calling GetType on a Nullable type causes a boxing operation to be performed when the type is implicitly converted to Object. Therefore GetType always returns a Type object that represents the underlying type, not the Nullable type.

在可空类型上调用GetType会导致在类型被隐式转换为对象时执行装箱操作。因此,GetType总是返回一个表示底层类型的类型对象,而不是可空类型。

When you box a nullable object, only the underlying type is boxed.

当您装箱一个可空对象时,只有底层类型被装箱。

Again, from MSDN :

再次,从MSDN:

Boxing a non-null nullable value type boxes the value type itself, not the System.Nullable that wraps the value type.

装箱一个非空值类型的非空值类型是值类型本身,而不是系统。对值类型进行包装的Nullable。

#2


22  

Further to Romain's correct answer, if you want to compare the "real" types (ie, without implicitly converting any nullable type to its underlying type) then you can create an extension method like so:

对于Romain的正确答案,如果您想要比较“真正的”类型(例如,不隐式地将任何可空类型转换为其底层类型),那么您可以创建这样的扩展方法:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        return typeof(T);
    }
}

And then try the following tests:

然后试试下面的测试:

int? a = 0;
Console.WriteLine(a.GetRealType() == typeof(int?));         // True
Console.WriteLine(a.GetRealType() == typeof(int));          // False

int b = 0;
Console.WriteLine(b.GetRealType() == typeof(int));          // True
Console.WriteLine(b.GetRealType() == typeof(int?));         // False

DateTime? c = DateTime.Now;
Console.WriteLine(c.GetRealType() == typeof(DateTime?));    // True
Console.WriteLine(c.GetRealType() == typeof(DateTime));     // False

DateTime d = DateTime.Now;
Console.WriteLine(d.GetRealType() == typeof(DateTime));     // True
Console.WriteLine(d.GetRealType() == typeof(DateTime?));    // False

EDIT...

编辑……

For completeness -- and prompted by SLaks's comments below -- here's an alternative version that only uses the compile-time type when source is either null or Nullable<>; otherwise it uses GetType and returns the runtime type:

为了完整性,这里有一个替代版本,它只在源代码为空或可空<>时使用编译时类型;否则使用GetType并返回运行时类型:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        Type t = typeof(T);

        if ((source == null) || (Nullable.GetUnderlyingType(t) != null))
            return t;

        return source.GetType();
    }
}

#3


3  

Although C# pretends that value-type storage locations hold instances of types derived from System.ValueType, which in turn derives from System.Object, that isn't really true. Each type derived from System.ValueType actually represents two very different kinds of things:

尽管c#假装值类型存储位置包含来自系统的类型的实例。ValueType,它反过来源自系统。对象,这不是真的。每一种类型都源于系统。ValueType实际上代表了两种非常不同的东西:

  1. A collection of bytes which (for primitive types) represents the data directly, or (for non-primitive structure types) holds the contents of all fields, public and private, but does not hold any type information.
  2. 直接表示数据的字节集合(对于基元类型),或者(对于非基元结构类型)包含所有字段(公共字段和私有字段)的内容,但不包含任何类型信息。
  3. A standalone heap object, which contains an object header in addition to the above, whose type is derived from `System.ValueType`.
  4. 一个独立的堆对象,除了上面的对象外,它还包含一个对象头,其类型派生自' System.ValueType '。

Storage locations of a value type hold the first; heap objects of a value type hold the second.

值类型的存储位置保存第一个值;值类型的堆对象保存第二个。

For various reasons, Microsoft decided that Nullable<T> should only support the first usage. If one attempts to pass a storage location of type Nullable<T> to code which expects a reference to a heap object, the system will convert the item to a T if HasValue is true, or else simply pass a null reference if HasValue is false. While there are ways to create a heap object of type Nullable<T>, the normal methods of converting a value-type storage location to a heap object will never generate one.

由于各种原因,Microsoft决定Nullable 应该只支持第一次使用。如果尝试将一个可空值 的存储位置传递给希望对堆对象引用的代码,系统将会将该项目转换为T,如果HasValue是true,或者如果HasValue是false,则只传递一个空引用。虽然有创建类型为Nullable 的堆对象的方法,但是将值类型存储位置转换为堆对象的常规方法永远不会生成堆对象。

Note also that calling GetType() on a value storage location won't actually evaluate the type of the storage location, but will instead convert the contents of that storage location to a heap object and then return the type of the resulting object. Because storage locations of type Nullable<T> get converted either to object instances of T or to null, nothing in an object instance will say whether the storage location from which it came was a Nullable<T>.

还要注意,在值存储位置上调用GetType()实际上不会计算存储位置的类型,而是将该存储位置的内容转换为堆对象,然后返回结果对象的类型。由于类型为Nullable 的存储位置被转换为T的对象实例或null,因此任何对象实例中都不会说它来自的存储位置是否为Nullable

#4


1  

A simple way to check that is using the "is" operator:

一种简单的检查方法是使用“is”操作符:

(i is Nullable<int>) || (i is Nullable<long>) || (i is Nullable<float>) || (i is Nullable<short>)

I figured out ti reading these two MSDN pages:

我读了这两页MSDN:

http://msdn.microsoft.com/en-us/library/ms366789(v=vs.90).aspx

http://msdn.microsoft.com/en-us/library/ms366789(v =应用程序). aspx

http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx

http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx

Cheers!

干杯!

#1


53  

According to the MSDN :

根据MSDN:

Calling GetType on a Nullable type causes a boxing operation to be performed when the type is implicitly converted to Object. Therefore GetType always returns a Type object that represents the underlying type, not the Nullable type.

在可空类型上调用GetType会导致在类型被隐式转换为对象时执行装箱操作。因此,GetType总是返回一个表示底层类型的类型对象,而不是可空类型。

When you box a nullable object, only the underlying type is boxed.

当您装箱一个可空对象时,只有底层类型被装箱。

Again, from MSDN :

再次,从MSDN:

Boxing a non-null nullable value type boxes the value type itself, not the System.Nullable that wraps the value type.

装箱一个非空值类型的非空值类型是值类型本身,而不是系统。对值类型进行包装的Nullable。

#2


22  

Further to Romain's correct answer, if you want to compare the "real" types (ie, without implicitly converting any nullable type to its underlying type) then you can create an extension method like so:

对于Romain的正确答案,如果您想要比较“真正的”类型(例如,不隐式地将任何可空类型转换为其底层类型),那么您可以创建这样的扩展方法:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        return typeof(T);
    }
}

And then try the following tests:

然后试试下面的测试:

int? a = 0;
Console.WriteLine(a.GetRealType() == typeof(int?));         // True
Console.WriteLine(a.GetRealType() == typeof(int));          // False

int b = 0;
Console.WriteLine(b.GetRealType() == typeof(int));          // True
Console.WriteLine(b.GetRealType() == typeof(int?));         // False

DateTime? c = DateTime.Now;
Console.WriteLine(c.GetRealType() == typeof(DateTime?));    // True
Console.WriteLine(c.GetRealType() == typeof(DateTime));     // False

DateTime d = DateTime.Now;
Console.WriteLine(d.GetRealType() == typeof(DateTime));     // True
Console.WriteLine(d.GetRealType() == typeof(DateTime?));    // False

EDIT...

编辑……

For completeness -- and prompted by SLaks's comments below -- here's an alternative version that only uses the compile-time type when source is either null or Nullable<>; otherwise it uses GetType and returns the runtime type:

为了完整性,这里有一个替代版本,它只在源代码为空或可空<>时使用编译时类型;否则使用GetType并返回运行时类型:

public static class MyExtensionMethods
{
    public static Type GetRealType<T>(this T source)
    {
        Type t = typeof(T);

        if ((source == null) || (Nullable.GetUnderlyingType(t) != null))
            return t;

        return source.GetType();
    }
}

#3


3  

Although C# pretends that value-type storage locations hold instances of types derived from System.ValueType, which in turn derives from System.Object, that isn't really true. Each type derived from System.ValueType actually represents two very different kinds of things:

尽管c#假装值类型存储位置包含来自系统的类型的实例。ValueType,它反过来源自系统。对象,这不是真的。每一种类型都源于系统。ValueType实际上代表了两种非常不同的东西:

  1. A collection of bytes which (for primitive types) represents the data directly, or (for non-primitive structure types) holds the contents of all fields, public and private, but does not hold any type information.
  2. 直接表示数据的字节集合(对于基元类型),或者(对于非基元结构类型)包含所有字段(公共字段和私有字段)的内容,但不包含任何类型信息。
  3. A standalone heap object, which contains an object header in addition to the above, whose type is derived from `System.ValueType`.
  4. 一个独立的堆对象,除了上面的对象外,它还包含一个对象头,其类型派生自' System.ValueType '。

Storage locations of a value type hold the first; heap objects of a value type hold the second.

值类型的存储位置保存第一个值;值类型的堆对象保存第二个。

For various reasons, Microsoft decided that Nullable<T> should only support the first usage. If one attempts to pass a storage location of type Nullable<T> to code which expects a reference to a heap object, the system will convert the item to a T if HasValue is true, or else simply pass a null reference if HasValue is false. While there are ways to create a heap object of type Nullable<T>, the normal methods of converting a value-type storage location to a heap object will never generate one.

由于各种原因,Microsoft决定Nullable 应该只支持第一次使用。如果尝试将一个可空值 的存储位置传递给希望对堆对象引用的代码,系统将会将该项目转换为T,如果HasValue是true,或者如果HasValue是false,则只传递一个空引用。虽然有创建类型为Nullable 的堆对象的方法,但是将值类型存储位置转换为堆对象的常规方法永远不会生成堆对象。

Note also that calling GetType() on a value storage location won't actually evaluate the type of the storage location, but will instead convert the contents of that storage location to a heap object and then return the type of the resulting object. Because storage locations of type Nullable<T> get converted either to object instances of T or to null, nothing in an object instance will say whether the storage location from which it came was a Nullable<T>.

还要注意,在值存储位置上调用GetType()实际上不会计算存储位置的类型,而是将该存储位置的内容转换为堆对象,然后返回结果对象的类型。由于类型为Nullable 的存储位置被转换为T的对象实例或null,因此任何对象实例中都不会说它来自的存储位置是否为Nullable

#4


1  

A simple way to check that is using the "is" operator:

一种简单的检查方法是使用“is”操作符:

(i is Nullable<int>) || (i is Nullable<long>) || (i is Nullable<float>) || (i is Nullable<short>)

I figured out ti reading these two MSDN pages:

我读了这两页MSDN:

http://msdn.microsoft.com/en-us/library/ms366789(v=vs.90).aspx

http://msdn.microsoft.com/en-us/library/ms366789(v =应用程序). aspx

http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx

http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx

Cheers!

干杯!