0

I have been trying for hours and a lot recode but can get rid of the CA1067 violation.
Using:
Visual Studio 2022, .Net v6.0.13, VB.NET

I will appreciate any help to solve the issue and insights in what I am doing wrong.

So the case is the following:
I have a template class SimNode(Of P,A) where P stands for the data type for parent and A for the data type of the three attributes of the node.

Public Class SimNode(Of P, A)
    Implements ISimNode(Of P, A)
    Implements IEquatable(Of SimNode(Of P, A))

    '[a bunch of properties and methods]

    Public Overridable Shadows Function Equals(other As SimNode(Of P, A)) As Boolean Implements IEquatable(Of SimNode(Of P, A)).Equals
        If Not Parent.Equals(other.Parent) Then Return False
        If Depth <> other.Depth Then Return False
        ....
        Return True
    End Function
End Class

I then needed to create another class called SimNode which inherits from SimNode(UShort,UShort) and requires an IEquatable(Of SimNode) because only unique SimNode instances will be added into a template 'container' -> Container(Of T as IEquatable(Of T)).
The word container is generic it could be e.g. a list, a dictionary or a hashset.

This new class is exactly the same as the parent class but with an extra member (list).

Private Class SimNode
    Inherits SimNode(Of UShort, UShort)
    Implements IEquatable(Of SimNode)

    '[a bunch of properties and methods]

    Private Shadows Function Equals(other As SimNode) As Boolean Implements IEquatable(Of SimNode).Equals
        Return MyBase.Equals(other)
    End Function
End Class

My equality criteria is still the same as the one in the parent class despite the extra list.
This approach is leading to a CA1067 violation and I just cannot get this correct.

I will appreciate very much any help!

I have try to follow the suggestions from Visual Studio but all lead to error. The suggestion of override the method Equals in the child class (SimNode) will produce obviously error because it can't override the base class since they have different signatures.

I also worked around this https://stackoverflow.com/questions/2441346/cascading-iequatableof-t with no success.

  • 1
    You don't appear to have overridden `Equals(Object)` in either class, which is what CA1067 is about. (I would also mention that it's quite odd for a non-generic type to derive from a generic type of the same name; the *reverse* is reasonably common, e.g. for `IEnumerable`.) – Jon Skeet Jan 25 '23 at 17:18
  • Thank you for answering! This may be silly but you may know better. What is the point to overriden Equals(object) when I know exactly the types? – Paulo Borges Jan 25 '23 at 17:26
  • You can overload it, e.g., `Public Overloads Function Equals(other As SimNode(Of P, A)) As Boolean Implements IEquatable(Of SimNode(Of P, A)).Equals ... `, but not shadow it. You can override `Function Equals(obj As Object) As Boolean`, if you need to – Jimi Jan 25 '23 at 17:30
  • But you probably should, it's required in some use cases – Jimi Jan 25 '23 at 17:36
  • I suspect you don't actually want to give a different answer if someone calls the `Equals(Object)` overload to the more specific one... – Jon Skeet Jan 25 '23 at 17:46
  • I see now what you mean! So, I should implement Equals to the specific type and to the object by overloading? Will try this but still wonder if that will solve the CA1067 violation. – Paulo Borges Jan 25 '23 at 18:14
  • Note that when you implement a routine on an interface in VB, you are allowed to give it a different name. e.g. you can call it `IEquatable_Equals` (which is roughly what happened in the VBA-heritage versions of VB). – Craig Jan 25 '23 at 18:25

1 Answers1

0

After all the great feedback I came up to the answer that solved the violation. The code is a bit out of how I would code but it seems that this is the correct way to do. Some feedback on the answer will be great to know if I did this correctly and if this is what you guys were trying to tell me! ;)

Module Program
    Sub Main()
        Dim storage As New Container(Of Y)
    End Sub

    Private Class Container(Of T As IEquatable(Of T))

    End Class

    Private Class X(Of P, A)
        Implements IEquatable(Of X(Of P, A))

        Public ReadOnly Parent As P
        Public ReadOnly Attribute As A

        Public Sub New(parent As P, attribute As A)
            Dim typ As Type : Dim datTyp As Integer
            Dim acceptingTypes As Integer() = {6, 8, 10, 12} '{Byte, USHort, UInteger, ULong}
            'check the type of P.
            typ = GetType(P) : datTyp = Type.GetTypeCode(typ)
            If Not acceptingTypes.Contains(datTyp) Then Throw New ArgumentException("Type 'P' is not acceptable.", NameOf(P))
            'check the type of A.
            typ = GetType(A) : datTyp = Type.GetTypeCode(typ)
            If Not acceptingTypes.Contains(datTyp) Then Throw New ArgumentException("Type 'A' is not acceptable.", NameOf(A))

            Me.Parent = parent : Me.Attribute = attribute
        End Sub

        Public Overridable Function IEquatable_Equals(other As X(Of P, A)) As Boolean Implements IEquatable(Of X(Of P, A)).Equals
            If Not Parent.Equals(other.Parent) Then Return False
            If Not Attribute.Equals(other.Attribute) Then Return False

            Return True
        End Function

        Public Overrides Function Equals(obj As Object) As Boolean
            Return DirectCast(Me, IEquatable(Of X(Of P, A))).Equals(TryCast(obj, X(Of P, A)))
        End Function

        Public Overrides Function GetHashCode() As Integer
            Return Parent.GetHashCode() + Attribute.GetHashCode()
        End Function
    End Class

    Private Class Y
        Inherits X(Of UShort, UShort)
        Implements IEquatable(Of Y)

        Public ReadOnly Lines As List(Of Integer)

        Public Sub New(parent As UShort, attribute As UShort)
            MyBase.New(parent, attribute)
            Lines = New List(Of Integer)
        End Sub

        Public Overloads Function Equals(other As Y) As Boolean Implements IEquatable(Of Y).Equals
            Return MyBase.IEquatable_Equals(other)
        End Function

        Public Overrides Function Equals(obj As Object) As Boolean
            Return DirectCast(Me, IEquatable(Of Y)).Equals(TryCast(obj, Y))
        End Function

        Public Overrides Function GetHashCode() As Integer
            Return Parent + Attribute 'Or Mybase.GetHashCode()?
        End Function
    End Class
End Module
  • I'd consider renaming `X_Equals` to `IEquatable_Equals`. This convention makes it clear what the purpose is (to provide an implementation of `Equals` from `IEquatable`). It's also consistent with how implementations are named in VBA-heritage versions of VB, though that's obviously not necessarily particularly compelling (after all, in VBA-heritage VB it was common to use the broken version of Hungarian variable names). – Craig Jan 25 '23 at 19:45
  • Yes. The example is to make emphasis on the name of the base class, Maybe a bit easier to follow the example. Thanks for the feedback! – Paulo Borges Jan 25 '23 at 19:54
  • If you delegate `GetHashCode` to your base class, you must also delegate `Equals` to your base class. If you want a different logic for `Equals`, you must provide your own `GetHashCode` accordingly. Otherwise many things will break. – GSerg Jan 25 '23 at 20:14
  • Guys, I am so thankful for all your feedback! And I am leaning with all these answers. I will try to make the corrections, in the answer. Just keep point whatever you find! :D – Paulo Borges Jan 25 '23 at 20:41
  • @GSerg Actually, I am not sure I am getting it. By base class is X and child is Y. I don't think I am delegating Y.GetHashCode() to the base class. X.GetHashCode() uses the Mybase.GetHashCode because I don't know yet which types P and A will take. Could you please elaborate a bit more. – Paulo Borges Jan 25 '23 at 21:15
  • `GetHashCode` must return the same value for any two objects for which `Equals` would return `True`. You have overridden `X.Equals`, it now returns `True` according to some logic. So `X.GetHashCode` must calculate its value according to the same logic, in such way that it would return the same integer for any two objects deemed equal by your `X.Equals`. – GSerg Jan 25 '23 at 22:53
  • Thank you for answering. I think I got it now and clearly makes a lot of sense. Thanks! I edit a bit further the answer which I think now reflects what you suggested. P and A can only be {Byte, USHort, UInteger, ULong} via constructor. – Paulo Borges Jan 26 '23 at 06:04