How can I re-write this conditional logic that involves checking three Nullable(Of T) values in an equality operator overload?
So I'm writing the equality operator overload (Operator =()
)for a custom object, and the resulting mess of If conditionals is just an eyesore. But so far, it seems like the only sane way to check the values to as to match the specific behavior of this object.
The rules are:
- Num1 is required, period, and both the left and right operands and must be equal for True. Else, False.
- Num2 is optional, but if specified, must be present for both the left and right operands, and must be equal for True. Else, False.
- Num3 is optional, but can only be specified if Num2 is also present. Else, False.
- Num3, if specified, must be present for both the left and right operands, and must be equal for True. Else, False.
Public Shared Operator =(ByVal lhOp As MyObj, ByVal rhOp As MyObj) As Boolean
If lhOp Is Nothing Then _
Return rhOp Is Nothing
If rhOp Is Nothing Then _
Return lhOp Is Nothing
Dim tmpBool As Boolean = ((lhOp.Num1.HasValue AndAlso rhOp.Num1.HasValue) AndAlso 开发者_开发知识库(lhOp.Num1.Value = rhOp.Num1.Value))
'// If tmpbool is 'True', then see if the Num2 HasValue params are equal.
If tmpBool Then
If (lhOp.Num2.HasValue = rhOp.Num2.HasValue) Then
'// They match. Now, do they have any data?
If (lhOp.Num2.HasValue AndAlso rhOp.Num2.HasValue) Then
'// Num2 has a value. See if the left and right operands
'// match each other.
If (lhOp.Num2.Value = rhOp.Num2.Value) Then
'// Num2 matches, so see if Num3 HasValue params
'// are equal.
If (lhOp.Num3.HasValue = rhOp.Num3.HasValue) Then
'// They match. Now, do they have any data?
If (lhOp.Num3.HasValue AndAlso rhOp.Num3.HasValue) Then
'// Num3 has a value. See if the left and right operands
'// match each other.
If (lhOp.Num3.Value = rhOp.Num3.Value) Then
'// All three parameters match, return 'True'.
Return True
Else
'// Num3 failed to match, return 'False'.
Return False
End If
Else
'// No data in Num3, return true.
Return True
End If
Else
'// Num3.HasValues do not match, return false.
Return False
End If
Else
'// Num2 failed to match, return 'False'.
Return False
End If
Else
'// No Num2, so we cannot have a Num3 specified as well.
If (lhOp.Num3.HasValue OrElse rhOp.Num3.HasValue) Then _
Return False
'// Return true.
Return True
End If
Else
'// Num2.HasValues do not match, return false.
Return False
End If
End If
'// Default is false.
Return False
End Operator
I can use ternary conditionals to collapse it down to the code block below, which looks more elegant, but is much more vague and doesn't make for proper maintainership:
Return If(((lhOp.Num1.HasValue AndAlso rhOp.Num1.HasValue) AndAlso
(lhOp.Num1.Value = rhOp.Num1.Value)),
If((lhOp.Num2.HasValue = rhOp.Num2.HasValue),
If((lhOp.Num2.HasValue AndAlso rhOp.Num2.HasValue),
If((lhOp.Num2.Value = rhOp.Num2.Value),
If((lhOp.Num3.HasValue = rhOp.Num3.HasValue),
If((lhOp.Num3.HasValue AndAlso rhOp.Num3.HasValue),
If((lhOp.Num3.Value = rhOp.Num3.Value),
True, False),
True),
False),
False),
If((lhOp.Num3.HasValue OrElse rhOp.Num3.HasValue), False, True)),
False),
False)
I also know that I could cache elements of the checks into booleans and then use those to reduce the amount of text. But that seems like a waste of cycles just for code readability. So I wanted to know if the SO community had better thoughts on reorganizing it, just in case I'm being too verbose with my checks.I think you'll find most people here that have been programming for a while will recommend returning from a method as soon as possible. Otherwise you end up indenting your code forever and trying to match up logic with End XXX or braces.
So this simplified class should roughly match yours:
Public Class MyObj
Public Property Num1 As Boolean?
Public Property Num2 As Boolean?
Public Property Num3 As Boolean?
Public Sub New(ByVal n1 As Boolean?, ByVal n2 As Boolean?, ByVal n3 As Boolean?)
Me.Num1 = n1
Me.Num2 = n2
Me.Num3 = n3
End Sub
End Class
This method should work as a comparer per your logic:
Public Shared Function CheckEquals(ByVal lhOp As MyObj, ByVal rhOp As MyObj) As Boolean
'//Num1 is required, also check the others for mismatched properties
If (Not lhOp.Num1.HasValue) OrElse (Not rhOp.Num1.HasValue) OrElse (lhOp.Num2.HasValue <> rhOp.Num2.HasValue) OrElse (lhOp.Num3.HasValue <> rhOp.Num3.HasValue) Then Return False
'//Num1 is required and must have the same value
If (lhOp.Num1.Value <> rhOp.Num1.Value) Then Return False
'//If Num2 is present the values must match
If (lhOp.Num2.HasValue) AndAlso (lhOp.Num2.Value <> rhOp.Num2.Value) Then Return False
'//Num3 is optional but Num2 must be set
If lhOp.Num3.HasValue AndAlso (Not lhOp.Num2.HasValue OrElse (lhOp.Num3.Value <> lhOp.Num3.Value)) Then Return False
Return True
End Function
And here's a bunch of tests that should prove it:
Debug.Assert(CheckEquals(New MyObj(True, Nothing, Nothing), New MyObj(True, Nothing, Nothing)) = True)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, Nothing), New MyObj(True, True, Nothing)) = False)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, Nothing), New MyObj(True, Nothing, True)) = False)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, Nothing), New MyObj(True, True, True)) = False)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, Nothing), New MyObj(True, False, Nothing)) = False)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, Nothing), New MyObj(True, Nothing, False)) = False)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, Nothing), New MyObj(True, False, False)) = False)
Debug.Assert(CheckEquals(New MyObj(True, True, Nothing), New MyObj(True, True, Nothing)) = True)
Debug.Assert(CheckEquals(New MyObj(True, True, Nothing), New MyObj(True, True, True)) = False)
Debug.Assert(CheckEquals(New MyObj(True, True, Nothing), New MyObj(True, True, False)) = False)
Debug.Assert(CheckEquals(New MyObj(True, True, Nothing), New MyObj(True, Nothing, True)) = False)
Debug.Assert(CheckEquals(New MyObj(True, True, Nothing), New MyObj(True, Nothing, False)) = False)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, True), New MyObj(True, Nothing, True)) = False)
Debug.Assert(CheckEquals(New MyObj(True, Nothing, True), New MyObj(True, Nothing, False)) = False)
Debug.Assert(CheckEquals(New MyObj(True, True, True), New MyObj(True, True, True)) = True)
Debug.Assert(CheckEquals(New MyObj(True, True, False), New MyObj(True, True, False)) = True)
Debug.Assert(CheckEquals(New MyObj(True, False, False), New MyObj(True, False, False)) = True)
Debug.Assert(CheckEquals(New MyObj(True, False, True), New MyObj(True, False, True)) = True)
精彩评论