Vb.NET - GetEnumerator on a Custom Template class - vb.net

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.

Related

Is there a way I can replace the 'Object' value type in this Visual Basic dictionary with something more precise?

I'm using a factory to select from several services that are implementing the same generic interface. I have the factory written like this:
Public Class JurisdictionServiceFactory
Private JurisdictionTypeMapper As Dictionary(Of String, Object)
Sub New()
JurisdictionTypeMapper = New Dictionary(Of String, Object)
JurisdictionTypeMapper.Add("CountryJurisdiction", ProjectGlobals.UnityContainer.Resolve(Of IJurisdictionService(Of CountryJurisdiction)))
JurisdictionTypeMapper.Add("StateJurisdiction", ProjectGlobals.UnityContainer.Resolve(Of IJurisdictionService(Of StateJurisdiction)))
JurisdictionTypeMapper.Add("CountyJurisdiction", ProjectGlobals.UnityContainer.Resolve(Of IJurisdictionService(Of CountyJurisdiction)))
JurisdictionTypeMapper.Add("CityJurisdiction", ProjectGlobals.UnityContainer.Resolve(Of IJurisdictionService(Of CityJurisdiction)))
JurisdictionTypeMapper.Add("OtherJurisdiction", ProjectGlobals.UnityContainer.Resolve(Of IJurisdictionService(Of OtherJurisdiction)))
End Sub
Public Function getJurisdictionService(type As String) As Object
Return JurisdictionTypeMapper(type)
End Function
End Class
I would like to replace 'Object' as the value type with something that would allow the compiler to know what methods exist on the object. For example, I want to be able to use autocomplete. I've tried doing this: Private JurisdictionTypeMapper As Dictionary(Of String, IJurisdictionService(Of )), but I just get a message: "Type expected".
This is IJurisdictionService:
Public Interface IJurisdictionService(Of t)
Inherits IDisposable
Function GetJurisdictionsByCompany(companyId As Integer) As List(Of t)
Sub AddJurisdiction(jurisdiction As t)
End Interface
This is an example implementation:
Public Class CountryJurisdictionService
Implements IJurisdictionService(Of CountryJurisdiction)
Private jurisdictionRepository As IRepository(Of CountryJurisdiction)
Public Sub New(jurisdictionRepository As IRepository(Of CountryJurisdiction))
Me.jurisdictionRepository = jurisdictionRepository
End Sub
Public Sub AddJurisdiction(jurisdiction As CountryJurisdiction) Implements IJurisdictionService(Of CountryJurisdiction).AddJurisdiction
jurisdictionRepository.Add(jurisdiction)
jurisdictionRepository.Commit()
End Sub
Public Function GetJurisdictionsByCompany(companyId As Integer) As List(Of CountryJurisdiction) Implements IJurisdictionService(Of CountryJurisdiction).GetJurisdictionsByCompany
Return jurisdictionRepository.GetMany(Function(j) j.CompanyID = companyId, False)
End Function
End Class
Edit:
This is the context that the factory will be used in:
Public Sub AddJurisdiction(jurisdiction)
Using jurisdictionService = jurisdictionServiceFactory.getJurisdictionService(TypeName(jurisdiction))
jurisdictionService.AddJurisdiction(jurisdiction)
End Using
End Sub
Who is calling getJurisdictionService? As long as the caller knows what Type they are requesting you can do something like this.
How about changing this:
Public Function getJurisdictionService(type As String) As Object
Return JurisdictionTypeMapper(type)
End Function
To this:
Public Function getJurisdictionService(Of T)() As IJurisdictionService(Of T)
Return DirectCast(JurisdictionTypeMapper(GetType(T)), IJurisdictionService(Of T))
End Function
And the the caller does this to get a strongly-typed service:
service = jurisdictionServiceFactory.getJurisdictionService(Of CountryJurisdictionService)()
And of course the dictionary needs to be keyed off of the Type instead of the class name:
Private JurisdictionTypeMapper As Dictionary(Of Type, Object)
Edit:
Given the new details you have provided, in that situation I often like to create base class or parent interface to create a sibling relationship between the generic classes, even if this is just for a bit of code-based documentation:
Public Interface IJurisdictionService
Inherits IDisposable
End Interface
Public Interface IJurisdictionService(Of t)
Inherits IJurisdictionService
Function GetJurisdictionsByCompany(companyId As Integer) As List(Of t)
Sub AddJurisdiction(jurisdiction As t)
End Interface
And that goes for the Jurisdiction objects as well, they could use a parent Interface or base class:
Public Interface IJurisdictionService
End Interface
Then Private JurisdictionTypeMapper As Dictionary(Of String, Object) becomes Private JurisdictionTypeMapper As Dictionary(Of String, IJurisdictionService)

How to implement LINQ functions in a VB.NET collection class

I have a collection class which is a wrapper for a
List(Of MyClass)
And the collection class implements things like add, count etc
Private lst As List(Of MyClass)
Public Function Count() As Long
Return lst.Count
End Function
I want to add the ability to do Linq queries on the collection class. So client could do something like this:
dim c as New MyCollectionClass
c.Add(New MyClass With {.Name = "XXX"})
c.Add(New MyClass With {.Name = "XXX"})
c.Add(New MyClass With {.Name = "YYYY"})
Dim nc As MyCollectionClass = c.GroupBy(function(x) x.Name)
How do I implement the groupby function and all the other Linq functions (select, filter, distinct, orderby etc) ? I've implemented iQueryable in the class.
I'm confused on how to declare it and its parameters and implementation etc. I've tried a few things like
Public Function GroupBy(f As Func(Of MyClass)) As IEnumerable
Return lst.GroupBy(Function(x As MyClass) f(x))
End Function
But thats just a syntax error, and I'm just stuck now :) thanks
FWIW this is what I've done for Iqueryable:
Implements IEnumerable, IQueryable
Private lst As New List(Of MyClass)
Public ReadOnly Property Expression As Expression Implements IQueryable.Expression
Get
Return lst.AsQueryable.Expression
End Get
End Property
Public ReadOnly Property ElementType As Type Implements IQueryable.ElementType
Get
Return lst.AsQueryable.ElementType
End Get
End Property
Public ReadOnly Property Provider As IQueryProvider Implements IQueryable.Provider
Get
Return lst.AsQueryable.Provider
End Get
End Property
You don't need to re-implement Linq extensions (Select, GroupBy, etc.). You just need to implement IEnumerable(Of MyClass), then standard Linq extensions will work with your class automatically.
An example of IEnumerable(Of ...) implementation:
Public Class MyCollection
Implements IEnumerable(Of MyClass1)
Private lst As New List(Of MyClass1)
Public Function GetEnumerator() As IEnumerator(Of MyClass1) Implements IEnumerable(Of MyClass1).GetEnumerator
Return lst.GetEnumerator()
End Function
Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
Return DirectCast(lst, IEnumerable).GetEnumerator()
End Function
End Class
And usage:
Dim collection = New MyCollection
...
Dim count = collection.Count()

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

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.