VB.Net Inheritance and Interfaces - vb.net

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.

Related

Vb.NET - GetEnumerator on a Custom Template class

I've got a generic class build like this
Public Class TabellaCustom(Of myType, TValue) Implements IEnumerable(Of TValue)
Private mKey As myType
Private mContenuto As TValue
...
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TValue) Implements System.Collections.Generic.IEnumerable(Of TValue).GetEnumerator
Return DirectCast(mContenuto, IEnumerator(Of TValue))
End Function
when I do something like this
dim Color as new ColorsEnumerable
Dim test(0) As StampeCommonFunctions.TabellaCustom(Of Color, String)
test(0) = New StampeCommonFunctions.TabellaCustom(Of Color, String)(Color.Red, "Red")
test.GetEnumerator()
I got an error:
Unable to cast object of type 'System.String' to type 'System.Collections.Generic.IEnumerator`1[System.String]'.
How can I solve this error? I must specify the type of object inside the class?
Well, mContenuto is a string, and you're trying to cast it into an IEnumerator(Of String), but the string class does not implement IEnumerator(Of String).
That's what the exception is telling you.
Your class seems to just hold two values (mKey, mContenuto), why do you want to implement IEnumerable<T> at all? It seems there's no need to this...
You could nonetheless implement GetEnumerator like this:
Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TValue) Implements System.Collections.Generic.IEnumerable(Of TValue).GetEnumerator
Return {Me.mContenuto}.AsEnumerable().GetEnumerator()
End Function
Private Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return GetEnumerator()
End Function
This works by creating an single-element array out of mContenuto and returning its Enumerator.

Implementing generic IComparer in VB

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.

Implementing inherited generic interface in vb.Net

In our current project I am using a generic interface iService which is inherited by all other service interfaces. For instance IService is inherited by ILogService.
The ILogService interface is then implemented by LogService as below:
Public Interface IService(Of T)
Sub Save(ByVal T As IEntity)
Sub Remove(ByVal T As IEntity)
Function FindBy(ByVal Id As Guid) As T
Function FindAll() As IEnumerable(Of T)
End Interface
Public Interface ILogService
Inherits IService(Of Log)
Function FindLogsByOwner(ByVal Owner As Guid, ByVal visibility As LogVisibility) As IList(Of Log)
Function FindAllLogsByVisibility(ByVal visibility As LogVisibility) As IList(Of Log)
Function FindAllLogsByType(ByVal type As LogType) As IList(Of Log)
End Interface
Public Class LogService
Implements ILogService
Public Function FindAll() As System.Collections.Generic.IEnumerable(Of Model.CSLS.Log) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).FindAll
End Function
Public Function FindBy(Id As System.Guid) As Model.CSLS.Log Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).FindBy
End Function
Public Sub Remove(T As Infrastructure.Domain.IEntity) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).Remove
End Sub
Public Sub Save(T As Infrastructure.Domain.IEntity) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).Save
End Sub
Public Function FindAllLogsByType(type As Model.CSLS.LogType) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindAllLogsByType
End Function
Public Function FindAllLogsByVisibility(visibility As Model.CSLS.LogVisibility) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindAllLogsByVisibility
End Function
Public Function FindLogsByOwner(Owner As System.Guid, visibility As Model.CSLS.LogVisibility) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindLogsByOwner
End Function
End Class
Help Needed: I am trying to understand that when I am implementing ILogService interface I still get the functions/subs in the LogService class containing:
method parameter T of Type IEntity instead of Log
How can I update the method signature so T is displayed as Log?
What am I doing wrong?
Are you talking about these?
Sub Save(ByVal T As IEntity)
Sub Remove(ByVal T As IEntity)
This is very confusing, because in the above methods T is the name of a method parameter, not a generic type parameter. It could just as easily have been foo or bar. In each case the type of T is IEntity.
If the intention here was that Save and Remove should each accept an argument of type T, but that type T must implement IEntity, this is how you would express that:
Public Interface IService(Of T As IEntity)
Sub Save(ByVal entity As T)
Sub Remove(ByVal entity As T)
Function FindBy(ByVal Id As Guid) As T
Function FindAll() As IEnumerable(Of T)
End Interface
#DanTao is correct if that is the intention. If, however, you just intended the name of the method parameter to be specified by the generic type, that is not possible.
However, you can specify any name you like in the implementing method, so you can use Log if you want, but you can't enforce it (and some FxCop rule will warn you haven't used the same parameter name as specified in the interface).

How to setup return value for a readonly property using RhinoMocks in VB.NET?

I'm using RhinoMock in VB.NET and I need to set the return value for a readonly list.
Here's what I want to do (but doesn't work):
dim s = Rhino.Mocks.MockRepository.GenerateStub(of IUserDto)()
s.Id = guid.NewGuid
s.Name = "Stubbed name"
s.Posts = new List(of IPost)
It fails on the compile because Posts is a readonly property.
Then I tried a lambda expression, which works fine for Function calls, but not so much for Properties. This fails to compile.
s.Stub(Function(x As IUserDto) x.Posts).Return(New List(Of IPost))
Next (failing) attempt was to use SetupResults, but this failed stating that it cannot be used in Playback mode.
Rhino.Mocks.SetupResult.For(s.Posts).Return(New List(Of IPost))
Which brings me back to my question:
How do I setup a return value for a readonly property using RhinoMocks in VB.NET?
Is IUserDto an interface? If it is then it should just work. If it isn't, then the problem could be that the readonly property in question is not overridable. RhinoMocks can only mock properties/methods which are defined in an interface or can be overridden.
Here is my (clumsy) attempt at proving that the lambda syntax should work:
Imports Rhino.Mocks
Public Class Class1
Public Sub Test()
Dim s = MockRepository.GenerateMock(Of IClass)()
Dim newList As New List(Of Integer)
newList.Add(10)
s.Stub(Function(x As IClass) x.Field).Return(newList)
MsgBox(s.Field(0))
End Sub
End Class
Public Class AnotherClass
Implements IClass
Public ReadOnly Property Field() As List(Of Integer) Implements IClass.Field
Get
Return New List(Of Integer)
End Get
End Property
End Class
Public Interface IClass
ReadOnly Property Field() As List(Of Integer)
End Interface
i.e. I would get a message box with the number 10 displayed on it (I didn't bother to try hooking up a unit-testing framework with this but that shouldn't make a difference) when Class1.Test is invoked.
Hope that helps (it was an interesting exercise trying to work with RhinoMocks in VB.NET in anycase).

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.