What does VB.Net For Each Loop look at to Infer the Type - vb.net

In the following code,
For Each item in MyCollection
...
Next
What does the compiler use to determine the type of item?
For example let say I have this class, which is inheriting a non generic collection,
Public Class BaseDataObjectGenericCollection(Of T)
Inherits BaseDataObjectCollection
End Class
A for each loop still infers the Item type as Object. How would I have to modify the above class to make the type inference work?
Edit: Per Beatles1692's answer, Implementing IEnumerator(Of T) kinda works. The base class already has a GetEnumerator function, inherited from CollectionBase, so I my implementation looked like this,
Public Function GetEnumerator1() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return MyBase.Cast(Of T)().GetEnumerator
End Function
However, the for loop still infers the type as object. But, if I change the interface implementation to this,
Public Shadows Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return MyBase.Cast(Of T)().GetEnumerator
End Function
That works, the for loop gets the type inference correct. So I guess the question is now, does For Each just look for a function called GetEnumerator ?

Well, there's only one place to go for a question like this. The spec!
Section 10.9.3 discusses For Each statements. According to it:
[if] local variable type inference is being used, then the identifier defines a new local variable whose scope is the entire For loop and whose type is the element type of the collection (Object if the enumerator expression is typed as Object).
"collection" here seems vague, but it's precisely defined on the next page. Essentially, the type must have a GetEnumerator() call, and this enumerator must (a) have a MoveNext() method that returns a boolean type, and (b) have a Current property. The type of the Current property is the type that will be inferred by the compiler. Note it actually has nothing to do with IEnumerator or IEnumerable...you just have to fit the prescribed pattern. Consider this code:
Option Infer On
Public Module M
Sub Main()
For Each x In New SomeClass()
Next
End Sub
End Module
Public Class SomeClass
Public Function GetEnumerator() As MyEnumerator
Return New MyEnumerator()
End Function
End Class
Public Class MyEnumerator
Public ReadOnly Property Current As Integer
Get
Return 42
End Get
End Property
Public Function MoveNext() As Boolean
Return True
End Function
End Class
The type of "x" in the Sub Main() is Integer, since the Current property returns Integer.

Either you should write :
For Each Item As SpecificType In MyCollection
....
Next
Then it will cast Item to SpecificType in each loop or your collection should have implemented IEnumerable(Of T)

Related

From A Method With In The Class Return An Instance Of The Class As An Interface Type That The Class Implements

what I'm trying to archive with the code below is to have the GetInstance generic function take in an interface type that SystemVars implements (say IAuthentication) then create an instance of SystemVars and return it as interface type T.
The problem I an having is that no matter what casting method I try I can't find a way to return the new instance of SystemVars as T. The line in the GetInstance method Return <CastingFunction>(New SystemVars,T) always fails to compile with the error message saying Value of type SystemVars cannot be converted to 'T'.
How do I return the instance of the class as the interface type that was passed into T?
Imports System.Drawing
Public Class SystemVars
Implements IAuthentication,
IAuthorization,
IApplicationStarting
Private Sub New()
End Sub
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(New SystemVars, T)
End Function
Public ReadOnly Property Username As String _
Implements IAuthentication.Username,
IAuthorization.Username
Get
Return _userName
End Get
End Property
Public ReadOnly Property Rolls As List(Of String) _
Implements IAuthorization.Rolls
Get
Return _rolls
End Get
End Property
Public ReadOnly Property InstallationId As Guid _
Implements IAuthentication.InstallationId,
IApplicationStarting.InstallationId
Get
Return _installationId
End Get
End Property
Public ReadOnly Property MainWindowStartUpPlacement As Rectangle _
Implements IApplicationStarting.MainWindowStartUpPlacement
Get
Return _mainWindowStartUpPlacement
End Get
End Property
'........
Private Shared _userName As String
Private Shared _rolls As List(Of String)
Private Shared _installationId As Guid
Private Shared _mainWindowStartUpPlacement As Rectangle
End Class
You can make an otherwise illegal cast work by passing through Object.
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(CObj(New SystemVars), T)
End Function
You will get a runtime error if the cast isn't possible; as noted in the comments, this strategy is chucking type safety out the window and basically telling the compiler, "Don't bother me, I know what I'm doing." The runtime will throw an InvalidCastException on failure if you don't test and throw yourself. You can test using Type.IsAssignableFrom if you want to create a more developer-friendly error message; there isn't much context available in the debugger at the point of failure, though it may be pretty obvious if you look up the call stack.
For just three interfaces, it might be better to do three separate specific functions rather than a generic version, especially considering that the functions are necessarily Shared (and thus can't themselves be part of an interface).
You might also consider a design that includes a Dependency Injection container. In this kind of design, there would be a configuration step that would associate the interfaces with SystemVars as the implementation, then the client would ask the container for an instance of the interface and receive a SystemVars object.
The rough way that the three options (the third being to cast the SystemVars object to the requested interface) would look in code is:
'Casting a received object to a requested interface
Dim asInterface = DirectCast(SystemVars.GetInstance(), IAuthorization)
'Using a custom casting function on SystemVars
Dim asInterface = SystemVars.GetInstance(Of IAuthorization)
'Using a DI container
'Behavior if the interface isn't supported depends on the container
Dim asInterface = container.GetInstance(Of IAuthorization)
Note that TryCast could be used instead of DirectCast, in which case the result would be Nothing if the interface isn't supported.

Pass an argument to a generic type New constructor in VB.Net Generics

I'm trying to be able to pass a Type parameter to a function called ConvertList, and have that function create some instances of the specified type. So, if I passed in type Foo, the function would create new objects of type Foo and put the created objects into a custom List object (SLMR_OBjList).
The function is in a generic class that is defined:
Public Class BOIS_Collection_Base(Of T)
The function would accept types other than what is passed in the class definition. So, if we create an instance of BOIS_Collection_Base(Of MyTypeA) we may call the function ConvertList(Of MyTypeB).
I want the private variable _convertedList to be of a different type than the class. Is this possible? I can only seem to define it with (Of T).
Here is what I have so far:
Public Class BOIS_Collection_Base(Of T)
Private _convertedList As SLMR_ObjList(Of T) ' I can only seem to define this as (Of T), but want to make sure I can pass in a Type other than the Base(Of T)
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of T) ' Should this be (Of T) or (Of myT) since I want it to use whatever Type is passed in
For Each tempVar In Me.ObjList
Dim newitem As myT = Activator.CreateInstance(GetType(myT), tempVar)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
Here is what I would like to be able to do:
Dim mainCollInstance As New BOIS_Collection_Base(Of MyTypeA)
....
'Code that populates the BOIS_Collection_Base.ObjList property with an SLMR_ObjList(Of MyTypeA)
....
' Now I want to take that ObjList, and cast all the items in it to MyTypeB
Dim newListObj As SLMR_ObjList(Of MyTypeB) = mainCollInstance.ConvertList(Of MyTypeB)
Is this possible? Am I going about it wrong?
In response to Plutonix:
If I define _convertedList inside the method, like this:
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of myT)
Dim _convertedList = New SLMR_ObjList(Of myT)
my errors go away, and the method does what I want, but _convertedList is no longer persistant in the object.
If you want to persist the list, then you can't really allow the consuming code to pass a different type for the list each time. That doesn't really make much sense, unless each time it's called, you only want the function to return the portion of the persisted list which contains objects of the given type. If that's the case, then you just need to declare _convertedList As SLMR_ObjList(Of Object) and then filter it and convert it to the correct type as necessary.
If, however, as I suspect is the case, the consumer will always be requesting that it be converted to the same type each time the function is called, then that output type is not really a property of the function call. Rather, it's a property of the whole class. In that case, you should make your class take two generic type arguments, like this:
Public Class BOIS_Collection_Base(Of T, TOut)
Private _convertedList As SLMR_ObjList(Of TOut)
Public Function ConvertedObjList() As SLMR_ObjList(Of TOut)
For Each tempVar As T In Me.ObjList
Dim newitem As TOut = DirectCast(Activator.CreateInstance(GetType(TOut), tempVar), TOut)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
End Class
Based on the previous related question and an assumption that MyTypeA and MyTypeB inherit from the same class (never got an answer), you may not need Generics for this. At any rate, this should help with the ctor part of the question. I do not as yet see where Generics fit in since inheritance may do what you want already:
Class MustInherit BiosItem
Public Property Name As String
Public Property TypeCode As String
...
MustOverride Function Foo(args...) As Type
Overridable Property FooBar As String
' etc - the more stuff in the base class the better
End Class
Class TypeA
Inherits ABClass
Public Sub New
MyBase.New ' stuff common to all child types
TypeCode = "A" ' EZ type ID rather than GetType
...
End Sub
End Class
Class TypeB would be the same, but initialize TypeCode to "B". The same for C-Z. These allow you to poll the object rather than needing GetType: If thisObj.TypeCode = "A" Then.... Now, the collection class:
Public Class BIOSItems
Inherits Collection(Of BiosItem)
' inheriting from Collection<T> provides Add, Count, IndexOf for us
' most important the Items collection
'
End Class
Typing the collection as BiosItem will allow TypeA or TypeJ or TypeQ in it. As is, your collection will hold one Type only as it should be. This works because an item which is GetType(TypeA) is also GetType(BiosItem). See also note at the end.
Converting one item to another would seem to be something that would largely be handled by the NEW item being created or converted to. Since they are likely to be very similar then it can be handled by a constructor overload (if they are not similar, well we are well down the wrong road):
' a ctor overload to create the new thing based on the old things props
Public Sub New(oldThing As BiosItem)
MyClass.New ' start with basics like TypeCode, MyBase.New
With BiosItem ' coversion
myFoo = .Foo
myBar = .Bar ' copy common prop vals to self
...
Select Case .TypeCode
Case "B"
myProp1 = .Prop33 ' conversions
myProp3 = .Prop16 + 3.14
...
End Select
' then initialize stuff unique to this type maybe
' based on other props
If .PropX = "FooBar" Then myPropZ = "Ziggy"
End With
End Sub
Code to create, convert, store:
Dim varOldBItem As TypeB = myBiosCol(ndx) ' get old item
Dim varAItem As New TypeA(varOldBItem) ' call the ctor above
myBiosCol.Add(varAItem) ' add new item
myBiosCol.Remove(varoldBItem) ' delete the old if need be
If BOIS_Collection_Base is always supposed to contain MyTypeA, then type it that way (inheriting from Collection<T> still seems in order). If also MyTypeB objects are never added to the collection directly, but converted to MyTypeA first (Edit makes that less clear), then most of the above still applies, except for the inheritance. A ctor overload on MyTypeA could still take an old B object and create itself based on it. I'd be less inclined to do it via the ctor if they do not inherit from the same base class, but it could be done.

VB typeof operator with generics

When comparing types in VB the following works as expected and enables the current instance to be compared against a specific inherited class, in this case returning False (snippet from LINQPad)
Sub Main
Dim a As New MyOtherChildClass
a.IsType().Dump()
End Sub
' Define other methods and classes here
MustInherit class MyBaseClass
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass
End Class
Class MyOtherChildClass
Inherits MyBaseClass
End Class
However when generics are introduced the VB compiler fails with the error Expression of type 'UserQuery.MyBaseClass(Of T)' can never be of type 'UserQuery.MyChildClass'.
' Define other methods and classes here
MustInherit class MyBaseClass(Of T)
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass(Of String)
End Class
Class MyOtherChildClass
Inherits MyBaseClass(Of String)
End Class
The equivalent code in C# compiles and allows the comparison, returning the correct result
void Main()
{
var a = new MyOtherChildClass();
a.IsType().Dump();
}
// Define other methods and classes here
abstract class MyBaseClass<T>
{
public bool IsType()
{
return this is MyChildClass;
}
}
class MyChildClass : MyBaseClass<string>
{
}
class MyOtherChildClass : MyBaseClass<string>
{
}
Why does the VB compiler not allow this comparison?
You raise an interesting point about VB/C# compilation that I can't really speak to. If you're looking for a solution, here's a way to do it from the question How can I recognize a generic class?
Define these functions:
Public Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type) As Boolean
Dim isParentGeneric As Boolean = parentType.IsGenericType
Return IsSubclassOf(childType, parentType, isParentGeneric)
End Function
Private Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type, ByVal isParentGeneric As Boolean) As Boolean
If childType Is Nothing Then
Return False
End If
If isParentGeneric AndAlso childType.IsGenericType Then
childType = childType.GetGenericTypeDefinition()
End If
If childType Is parentType Then
Return True
End If
Return IsSubclassOf(childType.BaseType, parentType, isParentGeneric)
End Function
Call like this:
Dim baseType As Type = GetType(MyBaseClass(Of ))
Dim childType As Type = GetType(MyOtherChildClass)
Console.WriteLine(IsSubclassOf(childType, baseType))
'Writes: True
Here's a Microsoft Connect Ticket that might deal with this issue and give some explanation as to whether this was a feature or a bug of generic typing.
Although this case doesn't seem supported by the Type Of documentation which states that for classes, typeof will return true if:
objectexpression is of type typename or inherits from typename
I'm familiar with C# but less so with VB. However, the example VB code and example C# code appear to be different. In the VB example you use Return TypeOf Me Is MyChildClass, which in C# would be return typeof(this) is MyChildClass;. But the (supposedly working) C# example just has return this is MyChildClass;.
I would expect that TypeOf Me Is MyChildClass is asking whether the instance expression on the left (which is a Type) can be assigned to a variable declared as the type on the right (MyChildClass). Since the framework class Type has no connection to your MyChildClass this is impossible and thus a likely mistake which the compiler can catch with a warning or error--possibly the one you're getting.
Instead, I would think that the VB code should be Return Me Is MyChildClass to match the C# example, which should correctly ask if the instance Me can be assigned to a variable declared as MyChildClass. Does VB still object if this syntax is used, or does that fix the error and get the correct behavior?

Issue with generics, interfaces, and casting

I recently added an interface to some custom user controls I have implemented. The interface is pretty basic. It has one method that supports chaining:
Public Interface IMyInterface(Of T As WebControl)
Function DoSomething() As T
End Interface
The implementations are also pretty basic:
Public Class MyCustomControl
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl)
Public Function DoSomething() As MyCustomControl _
Implements IMyInterface(Of MyCustomControl).DoSomething
' do stuff
Return Me
End Class
Everything works fine up to this point. The issues arise when I attempt to loop over a collection of controls that all implement the IMyInterface interface, like so:
Dim myList = New List(Of IMyInterface(Of WebControl))
myList.Add(someCustomControl)
myList.ForEach(Sub(i) i.DoSomething())
someCustomControl is a MyCustomControl which implements IMyInterface(Of MyCustomControl) instead of IMyInterface(Of WebControl).
I am getting this error on the second line (where I try to add someCustomControl):
Option Strict On disallows implicit conversions from 'MyCustomControl' to 'IMyInterface(Of WebControl)'.
Is there any way to get around this error? I am close to having it working but I do not know enough about generics to get beyond this point.
Covariance is a language feature that was introduced in VS 2010, and solves your problem. You need to define your generic such that the type T has the Out keyword in front of it:
Public Interface IMyInterface(Of Out T As WebControl)
Function DoSomething() As T
End Interface
When you use the Out keyword, you are using covariance. It allows generics of a more derived type to be used in place of a generic with the base type. So in your case it will allow a IMyInterface(Of MyCustomControl)) object in places where the code would normally expect IMyInterface(Of WebControl)), such as your for loop.
Note that covariance has a restriction. The covariant type T can only be used as a function return value, and not as a parameter into a function (or sub). For example, if the DoSomething signature in IMyInterface looked like this the compiler would complain:
' Here the type T is used as an input param - compiler error
Sub DoSomething(ByVal sampleArg As T)
Given your chaining scenario, I don't think the above restriction is a problem.
More Info at MSDN:
Covariance and Contravariance
Creating Variant Generic Interfaces
I don't know what your function DoSomething does, but I try assigning the instance's CssClass in there for testing purpose.
Declare the interface as follows:
Public Interface IMyInterface(Of Out T As WebControl)
Function DoSomething() As T
End Interface
Notice the Out T parameter.
Create 2 controls that implement the interface:
Public Class MyCustomControl1
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl1)
Public Function DoSomething() As MyCustomControl1 Implements IMyInterface(Of MyCustomControl1).DoSomething
' do stuff
Me.CssClass = "XXX"
Return Me
End Function
End Class
Public Class MyCustomControl2
Inherits CompositeControl
Implements IMyInterface(Of MyCustomControl2)
Public Function DoSomething() As MyCustomControl2 Implements IMyInterface(Of MyCustomControl2).DoSomething
' do stuff
Me.CssClass = "YYY"
Return Me
End Function
End Class
On a test page's PageLoad event:
Dim someCustomControl As New MyCustomControl1
Dim someCustomControl2 As New MyCustomControl2
Dim myList = New List(Of IMyInterface(Of WebControl))
myList.Add(someCustomControl)
myList.Add(someCustomControl2)
myList.ForEach(Sub(i) Literal1.Text &= i.DoSomething.CssClass & "<br />")
The result is, the CssClass property of both someCustomControl & someCustomControl2 are set to the respective values.
This shows that the interface function DoSomething was successfully called and the instance changed.
You will need to cast the object before adding it:
myList.Add(CType(someCustomControl, IMyInterface(Of WebControl)))
You may also want to concider making the interface not generic and your "DoWork" method return type as the interface itself.
Public Interface IMyInterface
Function DoSomething() As IMyInterface
End Interface
When you have to specify the type in the interface definition it kind of takes away from the power of interfaces (not having to know about the implementation).

vb.net method constraints

I have a class which I have marked as MustInherit (called BasePage), with a generic method that is marked as MustOverride:
Protected MustOverride Function SaveData(Of T As {BaseClass})(ByVal item As T) As T
What I want to do is force the users of this method to only supply a type of BaseClass, or anything derived from it. Also, when a class derives from BasePage, it should work on only one derived class from BaseClass:
Protected Overrides Function SaveData(Of T As BaseClass)(ByVal item As T) As T
Dim grad As DerivedClass = CType(item, DerivedClass)
Return grad
End Function
However, when I try to do the cast, it flags up the following error:
Value of type 'T' cannot be converted to 'DerivedClass'.
All the documentation I have read suggests that this should work. However, it's not a big problem if it doesn't work, as I can work around by making a non-generic method that only accepts BaseClass.
Any ideas?
All the documentation I have read suggests that this should work.
On the contrary: it can’t work. The type T derives from BaseClass – but nothing in your code tells the compiler that it is convertible to DerivedClass. For example, it could be of type IndependentlyDerivedClass which is a sibling of DerivedClass.
However, the following cast works:
Dim grad As DerivedClass = DirectCast(DirectCast(item, BaseClass), DerivedClass))
Notice that I’m using DirectCast in place of CType. This is a best-practice when casting in class hierarchies since DirectCast only allows such casts (these, and boxing/unboxing conversions) so you minimize the risk of accidentally calling a conversion operator (which can happen when you’re using CType on non-related types).
You typically do something like this
Public MustInherit Class BasePage(Of T As BaseClass)
Public MustOverride Function Savedata(ByVal Item As T) As T
End Class
Public Class derivedPage
Inherits BasePage(Of DerivedClass)
Public Overrides Function Savedata(ByVal Item As DerivedClass) As DerivedClass
Dim grad As DerivedClass = Item
Return grad
End Function
End Class
Public MustInherit Class BaseClass
End Class
Public Class DerivedClass
Inherits BaseClass
End Class