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
精彩评论