c# int? 可空值类型
其实质上是在Framework中增加了一个Nullable<T>的泛型结构类型。
[SerializableAttribute()] public struct Nullable<T> : IFormattable, IComparable, INullableValue where T : ValueType
看看上面的声明,我们可以确定,Nullable是一个值类型,而且限制类型参数为值类型。
另外C#在语法层面作了简化,见下面的代码。
int? x = 3; Nullable<int> y = new Nullable<int>(3);
看看反编译的结果。
Nullable<int> nullable1 = new Nullable<int>(3); Nullable<int> nullable2 = new Nullable<int>(3);
实际上,编译器会将缩写方式处理成完整的结构体创建代码。
Nullable包含两个有用的属性,HasValue用来判断类型是否为空,如果不为空则可以通过Value属性获取其基础类型的值。
当其值不为空时,可以直接使用其基础类型的运算符进行操作。GetValueOrDefault 方法可以获取值或者基础类型的缺省值。
int? x = 3; int y = 1; Console.WriteLine(x + y);
不过看看这段代码的反编译结果,你可能会发现一些问题。
Nullable<int> nullable1 = new Nullable<int>(3); int num1 = 1; Nullable<int> nullable2 = nullable1; int num2 = num1; Console.WriteLine( nullable2.get_HasValue() ? new Nullable<int>(nullable2.GetValueOrDefault() + num2) : new Nullable<int>());
原本简单的代码变得很复杂,编译器创建了新的Nullable对象,而且通过判断,如发现Nullable对象为空,则放弃加法操作,直接返回空。
继续看反编译的IL代码,还出现了box指令,因此Nullable类型的代价是很高的,如非必须,不要使用。
.entrypoint // Code Size: 63 byte(s) .maxstack 3 .locals init ( [mscorlib]System.Nullable`1<int32> nullable1, int32 num1, [mscorlib]System.Nullable`1<int32> nullable2, int32 num2, [mscorlib]System.Nullable`1<int32> nullable3) L_0000: nop L_0001: ldloca.s nullable1 L_0003: ldc.i4.3 L_0004: call instance void [mscorlib]System.Nullable`1<int32>::.ctor(!0) L_0009: nop L_000a: ldc.i4.1 L_000b: stloc.1 L_000c: ldloc.0 L_000d: stloc.2 L_000e: ldloc.1 L_000f: stloc.3 L_0010: ldloca.s nullable2 L_0012: call instance bool [mscorlib]System.Nullable`1<int32>::get_HasValue() L_0017: brtrue.s L_0025 L_0019: ldloca.s nullable3 L_001b: initobj [mscorlib]System.Nullable`1<int32> L_0021: ldloc.s nullable3 L_0023: br.s L_0033 L_0025: ldloca.s nullable2 L_0027: call instance !0 [mscorlib]System.Nullable`1<int32>::GetValueOrDefault() L_002c: ldloc.3 L_002d: add L_002e: newobj instance void [mscorlib]System.Nullable`1<int32>::.ctor(!0) L_0033: box [mscorlib]System.Nullable`1<int32> L_0038: call void [mscorlib]System.Console::WriteLine(object) L_003d: nop L_003e: ret
最后提一下 ?? 这个操作符。
The ?? defines a default value that is returned when a nullable type is assigned to a non-nullable type.
// ?? operator example. int? x = null; int y = x ?? -1; // Assigns y to -1 if x is null.
Nullable<T> Struct
Represents a value type that can be assigned null
.
public struct Nullable<T> where T : struct
- T
The underlying value type of the Nullable<T> generic type.
- Inheritance
Examples
The following code example defines three rows of a table in the Microsoft Pubs sample database. The table contains two columns that are not nullable and two columns that are nullable.
using System;
class Sample
{
// Define the "titleAuthor" table of the Microsoft "pubs" database.
public struct titleAuthor
{
// Author ID; format ###-##-####
public string au_id;
// Title ID; format AA####
public string title_id;
// Author ORD is nullable.
public short? au_ord;
// Royalty Percent is nullable.
public int? royaltyper;
}
public static void Main()
{
// Declare and initialize the titleAuthor array.
titleAuthor[] ta = new titleAuthor[3];
ta[0].au_id = "712-32-1176";
ta[0].title_id = "PS3333";
ta[0].au_ord = 1;
ta[0].royaltyper = 100;
ta[1].au_id = "213-46-8915";
ta[1].title_id = "BU1032";
ta[1].au_ord = null;
ta[1].royaltyper = null;
ta[2].au_id = "672-71-3249";
ta[2].title_id = "TC7777";
ta[2].au_ord = null;
ta[2].royaltyper = 40;
// Display the values of the titleAuthor array elements, and
// display a legend.
Display("Title Authors Table", ta);
Console.WriteLine("Legend:");
Console.WriteLine("An Author ORD of -1 means no value is defined.");
Console.WriteLine("A Royalty % of 0 means no value is defined.");
}
// Display the values of the titleAuthor array elements.
public static void Display(string dspTitle,
titleAuthor[] dspAllTitleAuthors)
{
Console.WriteLine("*** {0} ***", dspTitle);
foreach (titleAuthor dspTA in dspAllTitleAuthors) {
Console.WriteLine("Author ID ... {0}", dspTA.au_id);
Console.WriteLine("Title ID .... {0}", dspTA.title_id);
Console.WriteLine("Author ORD .. {0}", dspTA.au_ord ?? -1);
Console.WriteLine("Royalty % ... {0}", dspTA.royaltyper ?? 0);
Console.WriteLine();
}
}
}
// The example displays the following output:
// *** Title Authors Table ***
// Author ID ... 712-32-1176
// Title ID .... PS3333
// Author ORD .. 1
// Royalty % ... 100
//
// Author ID ... 213-46-8915
// Title ID .... BU1032
// Author ORD .. -1
// Royalty % ... 0
//
// Author ID ... 672-71-3249
// Title ID .... TC7777
// Author ORD .. -1
// Royalty % ... 40
//
// Legend:
// An Author ORD of -1 means no value is defined.
// A Royalty % of 0 means no value is defined.
Remarks
A type is said to be nullable if it can be assigned a value or can be assigned null
, which means the type has no value whatsoever. By default, all reference types, such as String, are nullable, but all value types, such as Int32, are not.
In C# and Visual Basic, you mark a value type as nullable by using the ?
notation after the value type. For example, int?
in C# or Integer?
in Visual Basic declares an integer value type that can be assigned null
.
The Nullable<T> structure supports using only a value type as a nullable type because reference types are nullable by design.
The Nullable class provides complementary support for the Nullable<T> structure. The Nullable class supports obtaining the underlying type of a nullable type, and comparison and equality operations on pairs of nullable types whose underlying value type does not support generic comparison and equality operations.
Fundamental Properties
The two fundamental members of the Nullable<T> structure are the HasValue and Value properties. If the HasValue property for a Nullable<T> object is true
, the value of the object can be accessed with the Value property. If the HasValue property is false
, the value of the object is undefined and an attempt to access the Value property throws an InvalidOperationException.
Boxing and Unboxing
When a nullable type is boxed, the common language runtime automatically boxes the underlying value of the Nullable<T> object, not the Nullable<T> object itself. That is, if the HasValue property is true
, the contents of the Value property is boxed. When the underlying value of a nullable type is unboxed, the common language runtime creates a new Nullable<T> structure initialized to the underlying value.
If the HasValue
property of a nullable type is false
, the result of a boxing operation is null
. Consequently, if a boxed nullable type is passed to a method that expects an object argument, that method must be prepared to handle the case where the argument is null
. When null
is unboxed into a nullable type, the common language runtime creates a new Nullable<T> structure and initializes its HasValue
property to false
.
.NET Framework 4.5.1 and Windows Runtime Components
Starting with the .NET Framework 4.5.1, you can include a Nullable<T> type as a member of a structure exported in a WinMD library. Previously, this was not supported.
Constructors
Nullable<T>(T) |
Initializes a new instance of the Nullable<T> structure to the specified value. |
Properties
HasValue |
Gets a value indicating whether the current Nullable<T> object has a valid value of its underlying type. |
Value |
Gets the value of the current Nullable<T> object if it has been assigned a valid underlying value. |
Methods
Equals(Object) |
Indicates whether the current Nullable<T> object is equal to a specified object. |
GetHashCode() |
Retrieves the hash code of the object returned by the Value property. |
GetValueOrDefault() |
Retrieves the value of the current Nullable<T> object, or the object's default value. |
GetValueOrDefault(T) |
Retrieves the value of the current Nullable<T> object, or the specified default value. |
ToString() |
Returns the text representation of the value of the current Nullable<T> object. |
Operators
Explicit(Nullable<T> to T) | |
Implicit(T to Nullable<T>) |