有趣的“Nullable”
众所周知,Nullable是允许对一些值类型的数据类型直接赋值Null(VB.NET中为Nothing)的特殊值类型。如果你反编译这个值类型,将会看到这样一个局面——
[C#]
public struct Nullable<T> where T: struct { private bool hasValue; internal T value; public Nullable(T value) { this.value = value; this.hasValue = true; } public bool HasValue { get { return this.hasValue; } } public T Value { get { if (!this.HasValue) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue); } return this.value; } } public T GetValueOrDefault() { return this.value; } public T GetValueOrDefault(T defaultValue) { if (!this.HasValue) { return defaultValue; } return this.value; } public override bool Equals(object other) { if (!this.HasValue) { return (other == null); } if (other == null) { return false; } return this.value.Equals(other); } public override int GetHashCode() { if (!this.HasValue) { return 0; } return this.value.GetHashCode(); } public override string ToString() { if (!this.HasValue) { return ""; } return this.value.ToString(); } public static implicit operator T?(T value) { return new T?(value); } public static explicit operator T(T? value) { return value.Value; } }
[VB.NET]
<Serializable, StructLayout(LayoutKind.Sequential), TypeDependency("System.Collections.Generic.NullableEqualityComparer`1"), TypeDependency("System.Collections.Generic.NullableComparer`1")> _ Public Structure Nullable(Of T As Structure) Private hasValue As Boolean Friend value As T Public Sub New(ByVal value As T) Me.value = value Me.hasValue = True End Sub Public ReadOnly Property HasValue As Boolean Get Return Me.hasValue End Get End Property Public ReadOnly Property Value As T Get If Not Me.HasValue Then ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue) End If Return Me.value End Get End Property Public Function GetValueOrDefault() As T Return Me.value End Function Public Function GetValueOrDefault(ByVal defaultValue As T) As T If Not Me.HasValue Then Return defaultValue End If Return Me.value End Function Public Overrides Function Equals(ByVal other As Object) As Boolean If Not Me.HasValue Then Return (other Is Nothing) End If If (other Is Nothing) Then Return False End If Return Me.value.Equals(other) End Function Public Overrides Function GetHashCode() As Integer If Not Me.HasValue Then Return 0 End If Return Me.value.GetHashCode End Function Public Overrides Function ToString() As String If Not Me.HasValue Then Return "" End If Return Me.value.ToString End Function Public Shared Widening Operator CType(ByVal value As T) As T? Return New T?(value) End Operator Public Shared Narrowing Operator CType(ByVal value As T?) As T Return value.Value End Operator End Structure
以上是Nullable提供的不同种类的方法(包含初始化构造函数,强制类型转换等)。不过有一个有趣的问题——
设想一下把null赋值给Nullable<int>,那么应该系统会调用(C#中,implicit;VB.NET中Widening)那个方法,生成一个新的T类型并传入value。现在问题在于——如果Nullable的泛型类型指定是int,那么对应构造函数的T也是int,你怎么把null赋值给int了呢?
在VB.NET中因为对任意一个值类型赋值Nothing,其实就是初始化这些变量,所以VB.NET很好理解;C#呢?C#是不允许把null赋值给一个值类型的呀!怎么办呢?微软“别出心裁”地在编译时做了手脚(CLR小动作哦!)——注意看,当你在C#中写了:
int? n = null;
其实编译器会将其编译为:
Nullable<int> n = new Nullable<int>();
通过调用默认构造函数来初始化这个int(也就是说——Nullable内部的那个value还是0,只不过HasValue标识默认设置为false,何况微软重写了ToString方法,导致该类型“可空”,说白了根本没有“可空”这回事情,也根本不存在“值类型”=null的东东!)