How to use the Type class as type in variable declaration - vb.net

I want to make the same operations on different lists of controls of specific types. I could do it one type at a time, but I figured: "There's a bunch of these, so why not do this in a loop?" and now of course I'm wondering where I went wrong. The problem can be reproduced easily:
Dim myType As Type = GetType(TextBox)
Dim newList As New List(Of myType) ' this is not allowed
This is the specific error message: BC30002: Type 'currentType' is not defined
Is there a way to use an object of the Type class as a type, or to do a similar maneuver which would let me do something like this:
For Each currentType As Type In {GetType(myCustomType), GetType(anotherCustomClassType)}
Dim newList As New List(Of currentType)
' do stuff
Next

No there isn't. When you create a generic List, you must specify a data type. A Type object is not a data type. It's an object that contains information about a data type. Basically, when you create a List(Of T) you can only fix T to be something that you could have passed to GetType in the first place. Both require data types.
You can't put data types in a list because they are not objects. What you could do is write a generic method that does what you want for one type, e.g.
Private Sub DoStuff(Of T)()
Dim newList As New List(Of T)
'Do stuff here.
End Sub
and then call that method and specify different generic types, e.g.
DoStuff(Of SomeType)()
DoStuff(Of SomeOtherType)()
'Etc.
You have to make individual calls though, because you need to specify the generic type.

Related

VBA: function that takes multiple types of input?

I have some user-defined types and a function for each that takes those types and does something to them.
The thing is though, I'd like to write one, generic function that takes any of those types, and then just uses TypeName to figure out which one it is, and treats it like it needs to.
How do I do this? I tried taking the parameter as Function Foo(Param As Object), but then it fails when I try to pass in a user-defined type into the function.
User Defined Types (UDT) are not classes (they are similar to Structures in C or Records in Pascal), therefore instances of an UDT are not objects - that's the reason you can not use them as an argument to your function foo.
VBA has a universal data type Variant. So when you have Function Foo(Param As Variant), you can pass anything to the function, Foo 3 or Foo ActiveSheet or Foo "Hello world". Anything, except a UDT... If you try so, you will get a compiler error stating "Only user defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions.":
Public Type tMyType
i As Integer
s As String
End Type
Sub Test
Dim bar as tMyType
' Compiler complains
foo bar
Dim myUniversalVariant as Variant
' Compiler complains also
myUniversalVariant = bar
End Sub
The official documentation of Variant states that "Variant types now support user-defined types.", however at least on Excel 2016 this is not working (or at least I was not able to do so).
Your best bet is to use Classes. If you're lazy, just create a class module (per Type) and put all members of the type as public members, nothing more needed (I will not start to describe the pros and cons of getter and setter). You just need to remember that now you have to use the New statement to create and Set to assign an instance.
In class module (I named it clsMyType):
public i As Integer
public s As String
In the regular module:
Sub Test
Dim bar as clsMyType
Set bar = new clsMyType
foo bar
Dim myUniversalVariant as Variant
Set myUniversalVariant = bar
foo myUniversalVariant
End Sub
Now depending if your function wants to receive only object or also other stuff (like integers or strings), parameter type can be Variant or Object
Function foo(Param as Object)
Debug.Print TypeName(Param)
End Function

GetType() Shows non-existent type?

As I am trying to figure out how to work with the Siemens Tia Portal Openness framework, I try to find an item in my Tia Portal project with the ControllerTarget type.
I try to find the items like this:
Imports Siemens.Engineering
Imports Siemens.Engineering.HW
Module Module1
Sub Main()
Dim myTiaPortal
myTiaPortal = New TiaPortal(TiaPortalMode.WithoutUserInterface)
'The portal is open, now create a project.
Dim tiaProject As Project
'Open the sample project:
Dim fileName As String
fileName = "C:\Path\To\Project\Sample_Project.ap13"
tiaProject = myTiaPortal.Projects.Open(fileName)
'Get the frist device from the project:
Dim tiaDevice As Device
tiaDevice = tiaProject.Devices.First
For Each item As IDeviceItem In tiaDevice.DeviceItems
Console.WriteLine(item.GetType())
Next
Console.ReadKey()
End Sub
End Module
This shows two items in the project:
Siemens.Engineering.HW.DeviceItemImplementation
Siemens.Engineering.HW.ControllerTargetImplementation
When I try to define an object of the type ControllerTargetImplementation I get the message that this datatype does not exist.
When I try to convert the item of type ControllerTargetImplementation to an object of type ControllerTarget, this seems to work perfectly.
Does this mean that the type returned by GetType() does not have to be the same as the actual type of the object? Is this normal? Or is this a strange thing in the openness platform?
When I try to define an object of the type ControllerTargetImplementation I get the message that this datatype does not exist.
Types can be internal to an assembly, which means that while they exist and things like GetType will show them to be there, you can't use them directly.
When I try to convert the item of type ControllerTargetImplementation to an object of type ControllerTarget, this seems to work perfectly.
Given the names involved here, it certainly sounds like ControllerTarget is the type being exposed to you, the consumer of the library, while the implementation of that type, perhaps a subclass or an implementation of an interface (ie is ControllerTarget a class or interface?) is hidden from you as you don't need to know about how it does it's job, nor interfere with it.
Does this mean that the type returned by GetType() does not have to be the same as the actual type of the object?
The actual type of the object is what is reported by GetType, but that doesn't mean that it's necessarily what you refer to it as. For instance, consider the following:
Class A
End Class
Class B
Inherits A
End Class
Sub Main
Dim obj as A = new B()
Console.WriteLine(obj.GetType())
End Sub
This will report obj as having a type of B (because that's the actual type we instantiated with new B()), even though it's stored against a variable of type A.

Why can't I have two methods with the same parameters but different returns?

.net allows...
Public Function One(A as Integer) As String...
Public Function One(B as String) As String...
It figures out which one to call by looking at the Type of the parameters, like...
Dim A As String = One(5)
Ok, so why can't you do this...
Public Function One(A as Integer) As String...
Public Function One(B as Integer) As Integer...
The same amount of information is available to the compiler...
Dim A As Integer = One(5) ' should know to call the second version
The specific problem I'm trying to solve is to return the Values in a Dictionary(Of Integer, MyClass) so that it is visible to COM Interop. To do that I simply return the .Values as IEnumerable. But then I lose the type inside my own code, which is a PITA. If I could have two methods I could have one that returns IEnumerable and another that returns List(Of MyClass) and the API would be the same in both places. I could have two different method names, but that kind of defeats the purpose.
I'm open to any solution that fixes the underlying problem... is there a single type I can return that avoids all the DirectCast in my own code, while still being visible to COM Interop?
Short answer : because the return type isn't included in the method's signature
the relevant part of the spec
The following are not part of a member's signature, and hence cannot be overloaded on:
Modifiers to a type member (for example, Shared or Private).
Modifiers to a parameter (for example, ByVal or ByRef).
The names of the parameters.
The return type of a method or the element type of a property.

Save a list of object types in a variable

I want to save a list of object types into a variable.
For example something like this
Dim allowedTypes As New List(Of Type)
allowedTypes.Add(TextBox)
The above produces an error, however I need to save a list of object types in this list so that I could compare the allowedTypes when creating elements dynamically via a loop.
Is this possible in Vb.Net (Any alternative suggestions are welcome).
Call GetType() to get Type object for the specified type :
Dim allowedTypes As New List(Of Type)
allowedTypes.Add(GetType(TextBox))
You'll need to use the GetType method, as in:
Dim allowedTypes as new List(Of Type)
allowedTypes.Add(GetType(TextBox))

Iterate through generic list of unknown type at runtime in VB.Net

Does anyone know how to iterate over a generic list if the type of that list isn't known until runtime?
For example, assume obj1 is passed into a function as an Object:
Dim t As Type = obj1.GetType
If t.IsGenericType Then
Dim typeParameters() As Type = t.GetGenericArguments()
Dim typeParam As Type = typeParameters(0)
End If
If obj is passed as a List(Of String) then using the above I can determine that a generic list (t) was passed and that it's of type String (typeParam). I know I am making a big assumption that there is only one generic parameter, but that's fine for this simple example.
What I'd like to know is, based on the above, how do I do something like this:
For Each item As typeParam In obj1
'do something with it here
Next
Or even something as simple as getting obj1.Count().
The method that iterates over your list can specify a generic type:
Public Sub Foo(Of T)(list As List(Of T))
For Each obj As T In list
..do something with obj..
Next
End Sub
So then you can call:
Dim list As New List(Of String)
Foo(Of String)(list)
This method makes the code look a little hairy, at least in VB.NET.
The same thing can be accomplished if you have the objects that are in the list implement a specific interface. That way you can populate the list with any object type as long as they implement the interface, the iteration method would only work on the common values between the object types.
If you know that obj is a Generic List. Then you're in luck.
Generic List implements IList and IEnumerable (both are non-generic). So you could cast to either of those interfaces and then For Each over them.
IList has a count property.
IList also has a Cast method. If you don't know the type to cast to, use object. This will give you an IEnumerable(Of object) that you can then start using Linq against.