有趣的“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的东东!)

posted @ 2012-06-04 14:16  Serviceboy  阅读(770)  评论(0编辑  收藏  举报