开发者

Constructor-chaining and null reference tests

How to test the null value BEFORE calling the other constructor?

Say:

  ' class MyHoyr '
  Public Sub New(ByVal myHour As MyHour)
    ' Can't doing it here !!!! '
    If myHour Is Nothing Then Throw New ArgumentNullException("myHour")

    ' Constructor call should be first 开发者_Python百科'
    Me.New(myHour._timeSpan)

    ' Here is too late... '
  End Sub


  Private Sub New(ByVal timeSpan As TimeSpan)
    '.... '
  End Sub


The way I do that in C# is by using a static method in the pipe, for example:

public MyHour(MyHour myHour) : this(GetTimeSpan(myHour))
{}

private static TimeSpan GetTimeSpan(MyHour myHour)
{
    if(myHour== null) throw new ArgumentNullException("myHour");
    return myHour._timeSpan;
}

private MyHour(TimeSpan timeSpan)
{...}

I assume you can do something very similar in VB. (shared methods?)

Reflector assures me this translates into:

Public Sub New(ByVal myHour As MyHour)
    Me.New(MyHour.GetTimeSpan(myHour))
End Sub

Private Sub New(ByVal timeSpan As TimeSpan)
End Sub

Private Shared Function GetTimeSpan(ByVal myHour As MyHour) As TimeSpan
    If (myHourIs Nothing) Then
        Throw New ArgumentNullException("myHour")
    End If
    Return myHour._timeSpan
End Function


An ugly workaround would be an extension method:

<Extension(), DebuggerNonUserCode()> _
Public Function EnsureNotNull(Of T As Class)(ByVal Value As T, _
                                             ByVal Arg As String) As T
    If Value Is Nothing Then Throw New ArgumentNullException(Arg)
    Return Value
End Function

Used like:

' class MyHour '
Public Sub New(ByVal myHour As MyHour)
    Me.New(myHour.EnsureNotNull("myHour")._timeSpan)

End Sub


A simple way to handle this is to use the idea of "named constructors", aka factory methods.

Public Shared Function Create (ByValue myHour As MyHour) As Foo
  If myHour Is Nothing Then Throw New ArgumentNullException("myHour")
  Return New Foo(myHour._timeSpan)
End Function

Private Sub New(ByVal timeSpan As TimeSpan)
  '.... '
End Sub

You can see similar examples with System.Drawing.Color.FromArgb which use factory methods to avoid ambiguity.


My original impulse was to suggest that you not even have the constructor that takes the MyHour instance. Instead, make the user check for Nothing outside of the class:

Public Function GetSomeClassInstance(ByVal mh As MyHour) As SomeClass

    If mh IsNot Nothing Then
        Return New SomeClass(mh.TimeSpan)
    Else
        Throw New ArgumentNullException("mh", "MyHour instance must not be Nothing)
    End If

End Function

However, using a private constructor to actually construct the object, like this, might work (not tested):

Public Class SomeClass
    Public Sub New(ByVal mh As MyHour)
        MyClass.New(Nothing, mh)
    End Sub

    Public Sub New(ByVal ts As TimeSpan)
        MyClass.New(ts, Nothing)
    End Sub

    Private Sub New(ByVal ts As TimeSpan?, ByVal mh As MyHour)
        Dim _timeSpanToUse As TimeSpan

        If ts IsNot Nothing Then
            _timeSpanToUse = ts.Value
        Else
            If mh IsNot Nothing Then
                _timeSpanToUse = mh.TimeSpan
            Else
                Throw New ArgumentNullException("mh", "The MyHour parameter was NULL")
            End If
        End If

        'Continue using _timeSpanToUse here...
    End Sub
End Class
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜