Implementing generic IComparer in VB - vb.net

I am trying to create a class implementing the generic IComparer of my own class "Stellungen" (which translates to positions, like on a chess or checkers board).
This is what I got:
Private Class comparer(Of Stellung)
Implements System.Collections.Generic.IComparer(Of Stellung)
Public Function Compare(x As Stellung, y As Stellung) As Integer Implements System.Collections.Generic.IComparer(Of Stellung).Compare
End Function
End Class
Problem is: inside the function I have no access to any fields of my class. If I start off with x. Intellisense will only give me .Equals, .GetHashCode - the methods you get on a type but not on an instance.
Visual Studio 10 also highights this, in the definition of the function the bits "x as Stellung" and "y as Stellung" are written in light blue, meaning it is a type and not an actual object.
So... what do I do?? How do I access the things I want to compare inside my class??
Thanks!

The fields are probably private and that is why you cant access them.
Make you classes implement the IComparable<T> interface. You can than use that in you comparer class.
Here is an example of a generic comparer class that compares objects that implements IComparable<T>.
Public Class GenericComparer(Of T As IComparable(Of T))
Inherits Comparer(Of T)
Public Overrides Function [Compare](ByVal x As T, ByVal y As T) As Integer
If (Not x Is Nothing) Then
If (Not y Is Nothing) Then
Return x.CompareTo(y)
End If
Return 1
End If
If (Not y Is Nothing) Then
Return -1
End If
Return 0
End Function
Public Overrides Function Equals(ByVal obj As Object) As Boolean
Dim comparer As GenericComparer(Of T) = TryCast(obj,GenericComparer(Of T))
Return (Not comparer Is Nothing)
End Function
Public Overrides Function GetHashCode() As Integer
Return MyBase.GetType.Name.GetHashCode
End Function
End Class
public class Stellung
Implements IComparable(Of Stellung)
Public Function CompareTo(ByVal value As Stellung) As Integer
'Here you should be able to access all fields.
End Function
End class

If you declare Private Class comparer(Of Stellung) then "Stellung" is just a placeholder for the type to use (like "T" in the tutorials).
Declare Private Class comparer, and Implements System.Collections.Generic.IComparer(Of Stellung) tells the compiler that you want to compare objects of type "Stellung", which btw makes the properties of Stellung visible in the editor.

Related

I am having a CA1067 violation on IEquatable(Of T) and can't solve it

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.
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

How can I add a method to an interface with the implementing class as argument type in VB.NET?

I want to write an interface that contains a method that has an argument of the same type like the implementing class.
Currently I'm using this:
Public Interface IContent(Of T)
Function ContentEquals(content As IContent(Of T)) As Boolean
End Interface
Public Class SpecificContent
Implements IContent(Of Specific)
Private m_Member As String
Public Function ContentEquals(content As IContent(Of Specific)) As Boolean Implements IContent(Of Specific).ContentEquals
' Actually I need to access content.m_Member here
' Of course this doesn't work
' since not every IContent(Of Specific) has m_Member
' just because SpecificContent does.
Return True
End Function
End Class
The problem is, that this interface definition requires the implementation of ContentEquals to accept any object of any type implementing IContent(Of Specific), not just SpecificContent what I actually want to define.
Is there a way to define a method in the interface I that enforces a method that has an parameter of A for A Implements I and B for B Impements I, i. e. something like content As MyClass?
No, there isn't. And frankly, it's a good thing.
Think about it - how would you be able to use such interface? The point of an interface is that you can call certain method regardless of the type of the object that actually implements the interface. If the interface contained a method bound to its implementor's type, in variable of which type would you store that interface? And if you could store it in a variable, how would you pass it to a method that is not aware about the impelementor, only knowing about the interface? How would that method be able to call the interface, what would it pass as a parameter?
For this kind of equality testing you're supposed to override the Object.Equals method, or introduce another overload of Object.Equals that explicitly accepts your own type. You can still use an interface, too, but the parameter will have to be of type Object, just like it is with Object.Equals.
I found out a proper way to achieve what I want. The solution is interface-inheritance and a second generic for the type you want as parameter type. I knew I had done something similar before, but it was in Java and I couldn't quite remember.
These are my interfaces:
Public Interface IContentFactory(Of T, S As IContent(Of T))
Function CreateFrom(obj As T) As IComparableContent(Of T, S)
End Interface
Public Interface IContent(Of T)
Sub ApplyTo(obj As T)
End Interface
Public Interface IComparableContent(Of T, S As IContent(Of T))
Inherits IContent(Of T)
Function ContentEquals(content As S) As Boolean
End Interface
In my code, I can use these interfaces for a generic content handling class:
Public Class ContentHistory(Of T, S As IContent(Of T))
Private m_Obj As T
Private m_Factory As IContentFactory(Of T, S)
Private m_History As Stack(Of IComparableContent(Of T, S))
Public Sub New(obj As T, factory As IContentFactory(Of T, S))
If obj Is Nothing Then Throw New ArgumentNullException("obj")
If factory Is Nothing Then Throw New ArgumentNullException("factory")
m_Obj = obj
m_Factory = factory
m_History = New Stack(Of IComparableContent(Of T, S))
End Sub
Public Sub Backup()
Dim currentContent = m_Factory.CreateFrom(m_Obj)
If m_History.Count = 0 OrElse Not m_History.Peek().ContentEquals(currentContent) Then
m_History.Push(currentContent)
End If
End Function
Private Sub Restore()
If m_History.Count > 0 Then
m_History.Pop().ApplyTo(m_Obj)
End If
End Function
End Class
Now I can implement specific classes for content-objects and their factories, like so:
Public Class SpecificContentFactory
Implements IContentFactory(Of Specific, SpecificContent)
Public Function CreateFrom(obj As Specific) As IComparableContent(Of Specific, SpecificContent) Implements IContentFactory(Of Specific, SpecificContent).CreateFrom
Return New SpecificContent(obj)
End Function
End Class
Public Class SpecificContent
Implements IComparableContent(Of Specific, SpecificContent)
Private ReadOnly m_Value As String
Friend Sub New(obj As Specific)
m_Value = obj.Value
End Sub
Public Sub ApplyTo(obj As Specific) Implements IContent(Of Specific).ApplyTo
obj.Value = m_Value
End Sub
Public Function ContentEquals(content As SpecificContent) As Boolean Implements IComparableContent(Of Specific, SpecificContent).ContentEquals
Return (content.m_Value = m_Value)
End Function
End Class
To setup this content handler, all I have to do is
Dim obj = New Specific()
Dim history = New ContentHistory(Of Specific, SpecificContent)(obj, New SpecificContentFactory())
and I can use it like
obj.Value = "OldValue"
history.Backup
obj.Value = "OldValue"
history.Backup ' Nothing happens
obj.Value = "NewValue"
history.Backup
obj.Value = "EvenNewerValue"
Dim value = obj.Value ' "EvenNewerValue"
history.Restore
value = obj.Value ' "NewValue"
history.Restore
value = obj.Value ' "OldValue"
history.Restore ' Nothing happens
value = obj.Value ' "OldValue"
As long as I provide the SpecificContent and SpecificContentFactory implementations, I can use the ContentHistory(Of Specific, SpecificContent) for any type as Specific that I like.

How do I make a method in a vb generic interface that returns an instance of the implementing class?

In Visual Basic, I want to create an interface that includes a function that returns an object of the implementing class. That is
public interface I
function maker as ???
end interface
public class X
implements I
function maker as X
return new X()
end function
end class
public class Y
implements I
function maker as Y
return new Y()
end function
end class
Is there a way to say that? I suppose I could always say that maker returns an I rather than an X or a Y, but then callers would have to cast it. I thought of defining the interface as
public interface I(of ii as I)
But the whole point in doing this was so I could create a generic class that uses an of I, and if I say that, then the compiler insists on an infinite regression of I(of I(of I...
Not exactly, but you can do this:
Public Interface I(Of T)
Function Maker() As T
End Interface
Public Class X
Implements I(Of X)
Public Function Maker() As X Implements I.Maker
Return New X()
End Function
End Class
Or, like this:
Public Interface I
Function Maker() As I
End Interface
Public Class X
Implements I
Public Function Maker() As I
Return New X()
End Function
End Class
Neither of these options force the derived class to return an instance of their own type, but they allow you to implement it that way.
This is where you need the "curiously recurring" type declaration:
Public Interface I(Of T As I(Of T))
Function Maker() As T
End Interface
Public Class C
Implements I(Of C)
Function Maker() As C Implements I(Of C).Maker
Return New C
End Function
End Class
Sub Main
Dim First As I(Of C) = New C
Dim Second As C = First.Maker
End Sub
As Steven says, this still doesn't force the declaring type to use itself as T, but at least now T definitely implements I.

VB.Net Inheritance and Interfaces

Has someone a hint what I'm doing wrong in VB.Net?
Module Module1
Interface ISearch(Of T As ISearchResult)
Function ids() As List(Of T)
End Interface
Interface ISearchResult
Function id() As String
End Interface
MustInherit Class BasicSearch(Of T As ISearchResult)
Implements ISearch(Of T)
MustOverride Function ids() As System.Collections.Generic.List(Of T) Implements ISearch(Of T).ids
End Class
Class Search
Inherits BasicSearch(Of SearchResult)
Implements ISearch(Of SearchResult)
Overrides Function ids() As System.Collections.Generic.List(Of SearchResult)
Return New List(Of SearchResult)
End Function
End Class
Class SearchResult
Implements ISearchResult
Public Function id() As String Implements ISearchResult.id
Return "id"
End Function
End Class
Sub Main()
Dim foo As New Search()
Dim bar As Object = foo
Dim foobar As ISearch(Of ISearchResult) = foo
End Sub
End Module
The third cast isn't working.
Why?
did I miss a oop lesson?
thanks
An ISearch(Of SearchResult) isn't an ISearch(Of ISearchResult) - they have different generic type parameters. Search is an ISearch(Of SearchResult).
Brian's answer covers the covariance, etc, stuff for .NET 4 that I'd planned to add to this question later (I wrote the initial answer quickly and then had to go offline - by the time I got back, Brian had answered)
To answer mr. moes comment - Imagine if ISearch had another method:
Sub AddID(ID as T)
and assuming we then implement that in Search (which, remember, is ISearch(Of SearchResult), so T is SearchResult). And assume we had something else that implements ISearchResult, say:
Public Class BadNews
Implements ISearchResult
Public Function id() As String Implements ISearchResult.id
Return "other"
Function
End Class
Now, if your cast worked, we could now call:
foobar.AddID(New BadNews)
But this can't work - the implementation of AddID that we're calling is the one implemented by Search - and that function is only expecting to receive objects of type SearchResult.
The cast does not work because foobar is a ISearch(Of ISearchResult) and foo is a ISearch(Of SearchResult which are not compatible and no implicit conversion operator exists.
.NET 4.0 introduced the concept of covariant and contravariant generic type parameters which can be used to solve this problem elegantly. If you tried to compile the code with VB 10 you would get this error.
error BC36757: 'Module1.Search' cannot be converted to
'Module1.ISearch(Of Module1.ISearchResult)'. Consider changing the 'T'
in the definition of 'Interface ISearch(Of T As
Module1.ISearchResult)' to an Out type parameter, 'Out T'.
Following the recommendation you can indicate that T is covariant in ISearchResult by using the new Out keyword.
Interface ISearch(Of Out T As ISearchResult)
Function ids() As List(Of T)
End Interface
Unfortunately we now get this error.
error BC36724: Type 'T' cannot be used in this context because 'T' is
an 'Out' type parameter.
The reason is because List(Of T) is not covariant itself. However, IEnumerable(Of T) is. So this would be okay.
Interface ISearch(Of Out T As ISearchResult)
Function ids() As IEnumerable(Of T)
End Interface
You would then need to make the same change from List(Of T) to IEnumerable(Of T) for the other declarations as well.

Custom Generic.IEqualityComparer(Of T) - Compiler Errors

I am trying to implement a simple IEqulityComparer to use with LINQ collections. I have written the following code which is reduced to its simplest form for discussion purposes...
Public Structure bob
Dim SiteID As Integer
Dim fred As String
End Structure
Public Class insCompare
Implements System.Collections.Generic.IEqualityComparer(Of bob)
Public Function Equals(ByVal x As bob, ByVal y As bob) As Boolean
Return IIf(x.SiteID = y.SiteID, True, False)
End Function
Public Function GetHashCode(ByVal x As bob) As Integer
Return x.SiteID.GetHashCode()
End Function
End Class
The problem that I have is that both functions throw the compiler warning "function 'getHashCode' (or 'Equals') shadows an overridable method in the base class 'Object'. To override the base class method, this method must be declared 'Overrides'."
However, if I declare them as Overrides, I get the error "function 'GetHashCode' cannot be declared Overrides because it does not override a function in the base class."!!
I am also getting a compiler error on the "Implements" line to the effect that I must implement "getHashCode" but I presume that is a result of the first problem.
All my research indicates that I should be ok - anyone got any clues please?
This is a late answer to the question but as per the documentation you can do use the following. Notice the inclusion of the Overloads keyword.
Public Class MyModelComparer
Implements Generic.IEqualityComparer(Of MyModel)
Public Overloads Function Equals(x As MyModel, y As MyModel) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of MyModel).Equals
' do compare
End Function
Public Overloads Function GetHashCode(obj As MyModel) As Integer Implements System.Collections.Generic.IEqualityComparer(Of MyModel).GetHashCode
' do hashcode
End Function
End Class
Ok, it seems to get sorted by renaming the functions and declaring them as "Implements", although I have seen dozens of examples on the Web where this has not been the case.
However I now get a runtime exception which I will post separately.
Public Class insCompare
Implements System.Collections.Generic.IEqualityComparer(Of Object)
Public Function Equals1(ByVal x As Object, ByVal y As Object) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Object).Equals
Return IIf(x.SiteID = y.SiteID, True, False)
End Function
Public Function GetHashCode1(ByVal x As Object) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Object).GetHashCode
Return x.SiteID.ToString.ToLower.GetHashCode()
End Function
End Class
I am getting same problem. I am converting my C# code to VB.Net; Even Adding the Implements didn't help;
Using a shadow or overload removes all warning and errors. I wonder what is difference in behavior in both cases.
If I specify Overrides, I get an error.
Not specifying any of (overrides, overloads, shadows) gives a warning.
' <summary>
' Pair Comparator for maintaining uniquness in results.
' </summary>
Public Class PairComparer
Implements IEqualityComparer(Of Pair)
Public Shadows Function Equals(ByVal x As Pair, ByVal y As Pair) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Pair).Equals
If x.first = y.first AndAlso x.second = y.second Then
Equals = True
ElseIf x.first = y.second AndAlso x.second = y.first Then
Equals = True
Else
Equals = False
End If
End Function
Public Overloads Function GetHashCode(ByVal obj As Pair) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Pair).GetHashCode
GetHashCode = obj.first + obj.second
End Function
End Class
You're getting the compiler error because you are in VB.NET, not C#. In C#, having a method with the same signature as an interface method you need to implement makes the compiler wire it up for you automatically.
VB.NET requires that you explicitly implement say what method is being implemented. You can use the same name (it's encouraged), you just have to have that 'implements' clause.