开发者

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:

  1. Num1 is required, period, and both the left and right operands and must be equal for True. Else, False.
  2. Num2 is optional, but if specified, must be present for both the left and right operands, and must be equal for True. Else, False.
  3. Num3 is optional, but can only be specified if Num2 is also present. Else, False.
  4. Num3, if specified, must be present for both the left and right operands, and must be equal for True. Else, False.

This is what I have thus far:

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)
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜