4
\$\begingroup\$

In order to implement my own Dictionary<TKey, TValue> in VB6, I've implemented a KeyValuePair class, which can accomodate any type as its key, or value.

This KeyValuePair class implements the IComparable and IEquatable interfaces which I'm also using in this custom List<T> implementation

KeyValuePair.cls

Private Type tKeyValuePair
    Key As Variant
    value As Variant
End Type

Private this As tKeyValuePair
Implements IComparable
Implements IEquatable
Option Explicit

Private Function IsComparable() As Boolean
    IsComparable = Not IsObject(this.Key) Or TypeOf this.Key Is IComparable
End Function

Public Property Get Key() As Variant
    If IsObject(this.Key) Then
        Set Key = this.Key
    Else
        Key = this.Key
    End If
End Property

Public Property Let Key(k As Variant)
    If IsEmpty(k) Then Err.Raise 5
    this.Key = k
End Property

Public Property Set Key(k As Variant)
    If IsEmpty(k) Then Err.Raise 5
    Set this.Key = k
End Property

Public Property Get value() As Variant
    If IsObject(this.value) Then
        Set value = this.value
    Else
        value = this.value
    End If
End Property

Public Property Let value(v As Variant)
    this.value = v
End Property

Public Property Set value(v As Variant)
    Set this.value = v
End Property

Public Function ToString() As String
    ToString = TypeName(Me) & "<" & TypeName(this.Key) & ", " & TypeName(this.value) & ">"
End Function

Private Function IComparable_CompareTo(other As Variant) As Integer

    Dim kvp As KeyValuePair
    Set kvp = other

    IComparable_CompareTo = ToString = kvp.ToString
    If Not IComparable_CompareTo Then Exit Function

    If IsObject(kvp.Key) Then
        IComparable_CompareTo = CompareReferenceTypes(this.Key, kvp.Key)
    Else
        IComparable_CompareTo = CompareValueTypes(this.Key, kvp.Key)
    End If

End Function

Private Function IEquatable_Equals(other As Variant) As Boolean

    Dim kvp As KeyValuePair
    Set kvp = other

    IEquatable_Equals = ToString = kvp.ToString
    If Not IEquatable_Equals Then Exit Function

    If IsObject(kvp.Key) Then
        IEquatable_Equals = EquateReferenceTypes(this.Key, kvp.Key)
    Else
        IEquatable_Equals = EquateValueTypes(this.Key, kvp.Key)
    End If

End Function

Private Function EquateReferenceTypes(value As Variant, other As Variant) As Boolean

    Dim equatable As IEquatable
    If IsObject(this.Key) And TypeOf this.Key Is IEquatable Then

        Set equatable = value
        EquateReferenceTypes = equatable.Equals(other)

    Else

        Debug.Print "WARNING: Reference type doesn't implement IEquatable, using reference equality."
        EquateReferenceTypes = (ObjPtr(value) = ObjPtr(other))

    End If

End Function

Private Function EquateValueTypes(value As Variant, other As Variant) As Boolean

    EquateValueTypes = (value = other)

End Function

Private Function CompareReferenceTypes(value As Variant, other As Variant) As Integer

    Dim comparable As IComparable

    If IsComparable Then

        Set comparable = value
        CompareReferenceTypes = comparable.CompareTo(other)

    Else

        Err.Raise 9 'object required
        Exit Function

    End If

End Function

Private Function CompareValueTypes(value As Variant, other As Variant) As Integer

    If value < other Then

        CompareValueTypes = -1

    ElseIf value > other Then

        CompareValueTypes = 1

    End If

End Function

In C#, a KeyValuePair is a struct, a value type - this implementation being a class, makes it analoguous to a reference type, are there any major drawbacks to this? If so, are there any viable alternatives?

\$\endgroup\$
4
\$\begingroup\$

That has been said before in previous reviews, there's not really a gain in having a Private Type that lets you define a Private this As tKeyValuePair, which forces you to use this. to access what would otherwise be private fields.

Actually there is one gain: doing that allows having a Key public member that encapsulates a Key private member, all without confusing poor old VB6 compiler.

Other than that, one has to see the List and possibly also the Dictionary implementation to see the benefits of the additional complexity introduced by IEquatable and IComparable interface implementations.

The code is fairly clean, straightforward and readable, breaking IEquatable and IComparable implementations into sub-functions respectively for reference and value types might be a little overboard, but it does make a nice abstraction level when one reads the implementation code.

One nitpick, the interface implementations and the corresponding sub-functions could declare a local result variable and assign the function's return value in only a single location:

Dim result As Boolean

If IsObject(kvp.Key) Then
    result = CompareReferenceTypes(this.Key, kvp.Key)
Else
    result = CompareValueTypes(this.Key, kvp.Key)
End If

IComparable_CompareTo = result
Dim result As Boolean

If IsObject(kvp.Key) Then
    result = EquateReferenceTypes(this.Key, kvp.Key)
Else
    result = EquateValueTypes(this.Key, kvp.Key)
End If

IEquatable_Equals = result
Dim result As Boolean
Dim equatable As IEquatable

If IsObject(this.Key) And TypeOf this.Key Is IEquatable Then
    Set equatable = value
    result = equatable.Equals(other)
Else
    Debug.Print "WARNING: Reference type doesn't implement IEquatable, using reference equality."
    result = (ObjPtr(value) = ObjPtr(other))
End If

EquateReferenceTypes = result

Looking more closely at the shape of the code, vertical whitespaces aren't all that consistent: there's more vertical whitespaces near the bottom of the code module, while the code near the top doesn't seem to follow the same spacing style. Minor nitpick, but for a small specialized class like this, it's an easy fix.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.