-
Generic type conversion function CType
I want to write a generic function to convert between arbitrary types, for example:
Code:
TheValue = CType(obj, MyClass)
TheValue2 = CType(obj, MyClass2)
The parameters of the function are as follows:
Code:
Public Function CType(ByVal Value As Variant, DestType As TypeOrClassName) As Variant
End Function
I'd like to hear everyone's advice. Thanks.
-
Re: Generic type conversion function CType
what u mean?
just
Set Obj = Nothing
Set Obj = New MyClass
Obj need to be Variant.
-
Re: Generic type conversion function CType
The CType operator in VB.Net is actually dependent on the operator itself being overloaded on the type being converted. Unfortunately, VB6 doesn't have operator overloading.
If this is for your transpiler. You could translate conversions between primitive types like Integers, Strings etc using normal VB6 conversion functions like CInt and CStr. For classes, you could define a conversion function in a module and your transpiler could render the conversion as a call to that function.
Take the following:-
Code:
Public Class Person
Public Sub New(firstName As String, lastName As String)
Me.FirstName = firstName
Me.LastName = lastName
End Sub
Public Property FirstName As String
Public Property LastName As String
Public Shared Narrowing Operator CType(ByVal p As Person) As String
Return $"{p.FirstName} {p.LastName}"
End Operator
End Class
The above is a VB.Net class that has the CType operator overloaded. It defines a conversion from Person to String to be used like this:-
Code:
Dim Nick As New Person("Nick", "Logan")
Dim s As String = CType(Nick, String)
Debug.WriteLine(s)
Which outputs this:-
VB6 doesn't have a CType operator and even if it did, the language doesn't support operator overloading. However, you can define the Person class like this:-
Code:
'==============================================
'Person.cls
'==============================================
Public FirstName As String
Public LastName As String
And then you generate a conversion function in a separate module like this:-
Code:
'==============================================
'Conversions.bas
'==============================================
Public Function Conv_Person_To_String(ByVal p As Person) As String
Conv_Person_To_String = p.FirstName & " " & p.LastName
End Function
Which you then use like this:-
Code:
Dim Nick As New Person
Dim s As String
Nick.FirstName = "Nick"
Nick.LastName = "Logan"
s = Conversions.Conv_Person_To_String(Nick)
Debug.Print s
This is how an overloadable operator like CType could be transpiled from VB.Net to VB6.
-
Re: Generic type conversion function CType
Hi baka, Niya
When I use generic CType functions, I can achieve the following effects:
Code:
Public Sub DoTest()
Set TheValue = CType(SourceValue, "MyClass")
If IsEmptyOrNothing(TheValue) Then Exit Sub
TheValue2 = CType(SourceValue2, vbString)
If IsEmptyOrNothing(TheValue2) Then Exit Sub
TheValue3 = CType(SourceValue3, vbArray)
If IsEmptyOrNothing(TheValue3) Then Exit Sub
TheValue4 = CType(SourceValue4, "Guid")
If IsEmptyOrNothing(TheValue4) Then Exit Sub
TheValue5 = CType(SourceValue5, "BigInt")
If IsEmptyOrNothing(TheValue5) Then Exit Sub
'--- DoSomething ---
End Sub
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Niya
The CType operator in VB.Net is actually dependent on the operator itself being overloaded on the type being converted. Unfortunately, VB6 doesn't have operator overloading.
If this is for your transpiler. You could translate conversions between primitive types like Integers, Strings etc using normal VB6 conversion functions like CInt and CStr. For classes, you could define a conversion function in a module and your transpiler could render the conversion as a call to that function.
Take the following:-
Code:
Public Class Person
Public Sub New(firstName As String, lastName As String)
Me.FirstName = firstName
Me.LastName = lastName
End Sub
Public Property FirstName As String
Public Property LastName As String
Public Shared Narrowing Operator CType(ByVal p As Person) As String
Return $"{p.FirstName} {p.LastName}"
End Operator
End Class
The above is a VB.Net class that has the CType operator overloaded. It defines a conversion from Person to String to be used like this:-
Code:
Dim Nick As New Person("Nick", "Logan")
Dim s As String = CType(Nick, String)
Debug.WriteLine(s)
Which outputs this:-
VB6 doesn't have a CType operator and even if it did, the language doesn't support operator overloading. However, you can define the Person class like this:-
Code:
'==============================================
'Person.cls
'==============================================
Public FirstName As String
Public LastName As String
And then you generate a conversion function in a separate module like this:-
Code:
'==============================================
'Conversions.bas
'==============================================
Public Function Conv_Person_To_String(ByVal p As Person) As String
Conv_Person_To_String = p.FirstName & " " & p.LastName
End Function
Which you then use like this:-
Code:
Dim Nick As New Person
Dim s As String
Nick.FirstName = "Nick"
Nick.LastName = "Logan"
s = Conversions.Conv_Person_To_String(Nick)
Debug.Print s
This is how an overloadable operator like CType could be transpiled from VB.Net to VB6.
Since you are proficient in both dotNET and VB6, your code always inspires me a lot, thank you, Niya.
I want to simulate C#'s type system (reflection), but I looked at the source code of Type.cs and RuntimeType.cs of C#, and it seems very difficult to simulate C#'s reflection.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
SearchingDataOnly
and it seems very difficult to simulate C#'s reflection.
Reflection would probably one of the easiest things to implement. All you're doing is selectively exposing the data created by the parser. You could just use nodes from the AST to build metadata about classes, methods, and whatever else you want. You then use then expose this metadata through classes and methods in your language. It would probably be very tedious to actually implement but the underlying concept is very simple.
-
Re: Generic type conversion function CType
Not sure if this is what y'all are talking about, but: see here.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Elroy
Not sure if this is what y'all are talking about, but:
see here.
Something like that, however reflection in .Net and other modern languages can do typically do far more than just instantiate objects dynamically at runtime. Reflection allows you to enumerate over elements in the program itself. You can enumerate over types like classes and structs, get information about their methods like their names, parameters, return type etc. You can get information about classes themselves like if they are a subtype of another type, what interfaces they implement etc. You can also use it to create objects and call their methods. Reflections basically allows you to treat your program as a graph of objects from within itself.
-
Re: Generic type conversion function CType
Yes, that would be nice. I've often wanted an enumeration of the instantiated objects I have (without all the work of tracking them in the Initialize events.
Sort of like the Controls (or Forms) collection, but project wide.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Elroy
Yes, that would be nice. I've often wanted an enumeration of the instantiated objects I have (without all the work of tracking them in the Initialize events.
I think you misunderstand. No language I know allows explicit access to live objects. Reflection allows you access to the information about your program as defined by its source code:-
For example lets take these classes in VB.Net:-
Code:
Private Class Class1
Public Sub PrintValue()
Debug.WriteLine(23)
End Sub
End Class
Private Class Class2
Public Sub PrintValue()
Debug.WriteLine(45)
End Sub
End Class
Private Class Class3
Public Sub PrintValue()
Debug.WriteLine("Hello world.")
End Sub
End Class
You could instantiate and call their methods like this:-
Code:
Private Sub DoItNormally()
Dim c1 As New Class1
Dim c2 As New Class2
Dim c3 As New Class3
c1.PrintValue()
c2.PrintValue()
c3.PrintValue()
End Sub
That's pretty standard. It's just the normal way we create object and call their methods. However, this is fixed at compile time. With reflection, you can do this at runtime like this:-
Code:
Private Sub DoItUsingReflection()
Dim asm = Assembly.GetExecutingAssembly
'Enumerate all types in the assembly
For Each t As Type In asm.GetTypes
'If the current type is a class or structure whose
'declaration is nested inside this Form class
If t.DeclaringType Is Me.GetType Then
'Look for a method called PrintValue on the class
Dim m = t.GetMethod("PrintValue")
'If the method exists then....
If m IsNot Nothing Then
'Create an instance of the class
Dim c As Object = Activator.CreateInstance(t)
'Call the PrintValue method on the object
m.Invoke(c, {})
End If
End If
Next
End Sub
The above does the same thing but at runtime instead. I told it to search for all types nested inside that Form class, instantiate them and call their PrintValue method. If I were to add other nested classes to the form with PrintValue methods afterwards and re-compile, I wouldn't have to change that function at all because the new class would be enumerated at runtime.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
SearchingDataOnly
Hi baka, Niya
When I use generic
CType functions, I can achieve the following effects:
Code:
Public Sub DoTest()
Set TheValue = CType(SourceValue, "MyClass")
If IsEmptyOrNothing(TheValue) Then Exit Sub
TheValue2 = CType(SourceValue2, vbString)
If IsEmptyOrNothing(TheValue2) Then Exit Sub
TheValue3 = CType(SourceValue3, vbArray)
If IsEmptyOrNothing(TheValue3) Then Exit Sub
TheValue4 = CType(SourceValue4, "Guid")
If IsEmptyOrNothing(TheValue4) Then Exit Sub
TheValue5 = CType(SourceValue5, "BigInt")
If IsEmptyOrNothing(TheValue5) Then Exit Sub
'--- DoSomething ---
End Sub
Oh I missed this one. CType actually throws an exception if it cannot perform a conversion. You're actually thinking of the TryCast which works similarly to CType but returns Nothing if it fails to perform a conversion. TryCast only works where there is an inheritance relationship though so in that sense its more limited than CType.
-
Re: Generic type conversion function CType
I was recently in a big debate about generics and reflection at the tB GitHub discussion board. Try as I might, I don't see generics & reflection as an improvement over the COM contractual approach.
For example, why not something like this:
Code:
Dim t As Object
For Each t in SomeCollectionOfVariousTypes
If TypeOf t Is IPrintValue Then
t.PrintValue
End If
Next t
Or event better in a new VB/tB:
Code:
For Each t Of IMyDesiredInterface In SomeCollectionOfVariousTypes
t.PrintValue
Next
Why all the ceremony of this?
Code:
Private Sub DoItUsingReflection()
Dim asm = Assembly.GetExecutingAssembly
'Enumerate all types in the assembly
For Each t As Type In asm.GetTypes
'If the current type is a class or structure whose
'declaration is nested inside this Form class
If t.DeclaringType Is Me.GetType Then
'Look for a method called PrintValue on the class
Dim m = t.GetMethod("PrintValue")
'If the method exists then....
If m IsNot Nothing Then
'Create an instance of the class
Dim c As Object = Activator.CreateInstance(t)
'Call the PrintValue method on the object
m.Invoke(c, {})
End If
End If
Next
End Sub
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
jpbro
I was recently in a big debate about generics and reflection at the tB GitHub discussion board. Try as I might, I don't see generics & reflection as an improvement over the COM contractual approach.
All of my years going back and forth in VB6 vs VB.Net discussions hasn't convinced anyone who's mind was already made up so I'm not even going to pretend I can convince you. There is just no way I could relate you why it's a massive improvement in just one sentence or even a single paragraph. All I can tell you is that those of us that have spent years enjoying these features know why we love them and why it can be extremely painful using a language without them. I can't speak for all of "us" on this particular point but speaking for myself alone, you couldn't pay me to ever seriously write VB6 applications ever again. I don't mind doing stuff for the forums here and there, maybe even a CodeBank submission from time to time but there is nothing in the world that could motivate me to ever seriously go back to VB6. When you get used to the features of modern languages like generics, reflection and all the others I've mentioned in the past, it is damn near impossible to live without them. You actually feel it. It's very very suffocating.
Quote:
Originally Posted by
jpbro
For example, why not something like this:
Code:
Dim t As Object
For Each t in SomeCollectionOfVariousTypes
If TypeOf t Is IPrintValue Then
t.PrintValue
End If
Next t
Or event better in a new VB/tB:
Code:
For Each t Of IMyDesiredInterface In SomeCollectionOfVariousTypes
t.PrintValue
Next
That is not reflection. That code is just normal everyday collection enumeration that could be found in any language.
Reflection is about your program looking into itself and seeing itself. It can see it's own classes, interfaces, methods etc. Here's an exercise that could perhaps make it clearer what reflection is. Write a function in VB6, any function. Now at runtime I want you to have your program find this function and tell you how many parameters it has, and what the type of each parameter is, and what the return type of the function is. You should find that this is impossible in VB6. However, you can do it in VB.Net, C# and I believe even Python can do this. Here's what the above exercise would look like in VB.Net:-
Code:
Imports System.Reflection
Imports System.Text
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Debug.WriteLine(GetFuncInfo("JustSomeFunc"))
End Sub
Private Function GetFuncInfo(ByVal funcName As String) As String
Dim sb As New StringBuilder
Dim method = (From t In Assembly.GetExecutingAssembly.GetTypes
Let m = t.GetMethod(funcName, BindingFlags.NonPublic Or BindingFlags.Instance)
Where m IsNot Nothing
Select m).FirstOrDefault
If method IsNot Nothing Then
sb.AppendLine($"Method name : {method.Name}")
sb.AppendLine($"Number of parameters : {method.GetParameters.Length.ToString}")
sb.AppendLine($"Method return type : {method.ReturnType.Name}")
For Each p In method.GetParameters
sb.AppendLine($"{vbTab} Parameter name = '{p.Name}', Parameter type = {p.ParameterType.Name}")
Next
End If
Return sb.ToString
End Function
'Just an empty sample function for reflection to find.
Private Function JustSomeFunc(ByVal name As String, ByVal age As Integer, ByVal money As Double) As String()
End Function
End Class
The above code would output this:-
Code:
Method name : JustSomeFunc
Number of parameters : 3
Method return type : String[]
Parameter name = 'name', Parameter type = String
Parameter name = 'age', Parameter type = Int32
Parameter name = 'money', Parameter type = Double
The above code demonstrates true reflection. The program was able to look inside itself, find the JustSomeFunc function and print details about it at runtime. You could even call it if you want. If I wanted, I could enumerate over every function and sub in the entire program and print it out:-
Code:
Imports System.Reflection
Imports System.Text
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each t In Assembly.GetExecutingAssembly.GetTypes
For Each m In t.GetMethods(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.DeclaredOnly)
Debug.WriteLine(GetInfo(m))
Next
Next
End Sub
Private Function GetInfo(ByVal m As MethodInfo) As String
Dim sb As New StringBuilder
sb.AppendLine($"Method name : {m.Name}")
sb.AppendLine($"Number of parameters : {m.GetParameters.Length.ToString}")
sb.AppendLine($"Method return type : {m.ReturnType.Name}")
For Each p In m.GetParameters
sb.AppendLine($"{vbTab} Parameter name = '{p.Name}', Parameter type = {p.ParameterType.Name}")
Next
Return sb.ToString
End Function
End Class
The above outputs this:-
Code:
Method name : OnCreateMainForm
Number of parameters : 0
Method return type : Void
Method name : Form1_Load
Number of parameters : 2
Method return type : Void
Parameter name = 'sender', Parameter type = Object
Parameter name = 'e', Parameter type = EventArgs
Method name : GetInfo
Number of parameters : 1
Method return type : String
Parameter name = 'm', Parameter type = MethodInfo
Method name : Dispose
Number of parameters : 1
Method return type : Void
Parameter name = 'disposing', Parameter type = Boolean
Method name : InitializeComponent
Number of parameters : 0
Method return type : Void
Method name : Dispose__Instance__
Number of parameters : 1
Method return type : Void
Parameter name = 'instance', Parameter type = T&
Method name : GetType
Number of parameters : 0
Method return type : Type
Method name : GetType
Number of parameters : 0
Method return type : Type
Method name : Dispose__Instance__
Number of parameters : 1
Method return type : Void
Parameter name = 'instance', Parameter type = T&
Method name : get_GetInstance
Number of parameters : 0
Method return type : T
The above is selected information on every single sub and function declared in the program. If you look carefully, you will even see Form_Load sub and the GetInfo function there too.
VB6 cannot do this because the compiler must build the metadata from the abstract syntax tree its parser produces. Now you might be able to kinda fake this in VB6 if you can access COM IDLs but from what I understand, it was never designed with this intent so it will take some wizardry to do it. Even so, a COM IDL will never have anywhere near the amount of information that an AST does. It will still be limited.
-
Re: Generic type conversion function CType
BTW where was this generics/reflection discussion? I don't see anything of the sort on the GitHub page.
-
Re: Generic type conversion function CType
You can get all the classes, types, enums for public project types in vb6. Each public project contains a tlb with types descriptions.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
The trick
You can get all the classes, types, enums for public project types in vb6. Each public project contains a tlb with types descriptions.
I guessed it might be possible which is why I said this:-
Quote:
Originally Posted by
Niya
Now you might be able to kinda fake this in VB6 if you can access COM IDLs but from what I understand, it was never designed with this intent so it will take some wizardry to do it. Even so, a COM IDL will never have anywhere near the amount of information that an AST does. It will still be limited.
Also, could you also enumerate private UDTs, private methods on classes and private fields on classes? Can you also call public and private methods on classes through reflective means? I'm not being sarcastic or anything. I'm genuinely asking.
-
Re: Generic type conversion function CType
I'll answer later because I'm on the road.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Niya
Also, could you also enumerate private UDTs, private methods on classes and private fields on classes? Can you also call public and private methods on classes through reflective means? I'm not being sarcastic or anything. I'm genuinely asking.
Private's have a pointer, not a name.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
argen
Private's have a pointer, not a name.
We're not talking about looking for live data at runtime, we're talking program data as in data about the program itself like defined classes, methods etc, things that were written by the programmer.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
The trick
I'll answer later because I'm on the road.
Okay.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Niya
We're not talking about looking for live data at runtime, we're talking program data as in data about the program itself like defined classes, methods etc, things that were written by the programmer.
But for reflection you need the name, and there is no name for private definitions, just a pointer.
-
1 Attachment(s)
Re: Generic type conversion function CType
Quote:
Originally Posted by
Elroy
Not sure if this is what y'all are talking about, but:
see here.
Hi Elroy,
Your modNameBasedObjectFactory is very useful.
We know that we can use "TypeOf MyVar Is Class2" to determine whether MyVar is Class2, but I'd like to know if it is possible to achieve a similar "TypeOf MyVar Is Class2" effect by the class name "Class2". E.g:
Class1.cls
Code:
Option Explicit
Implements Class2
Public Name As String
Private m_nVersion As Long
Public Sub ChangeName()
m_nVersion = m_nVersion + 1
Name = Name & "_" + m_nVersion
End Sub
Private Sub Class2_ChangeName()
Me.ChangeName
End Sub
Class2.cls
Code:
Option Explicit
Public Sub ChangeName()
End Sub
Form1.frm
Code:
Option Explicit
Private Sub Form_Load()
Dim var As Variant
var = CType(GetTestResult(), "Class2")
If Not IsEmptyOrNothing(var) Then
MsgBox "var is Class2"
Else
MsgBox "var is not Class2"
End If
End Sub
Private Function CType(var As Variant, sClassName As String) As Variant
End Function
Private Function GetTestResult() As Variant
Dim cls As Class1
Set cls = New Class1
Set GetTestResult = cls
End Function
Private Function IsEmptyOrNothing(var) As Boolean
If IsEmpty(var) = True Then
IsEmptyOrNothing = True
ElseIf IsObject(var) = True Then
If var Is Nothing Then
IsEmptyOrNothing = True
End If
ElseIf IsArray(var) = True Then
If ArrIsNull(var) = True Then
IsEmptyOrNothing = True
End If
End If
End Function
Private Function ArrIsNull(var) As Boolean
End Function
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Niya
Something like that, however reflection in .Net and other modern languages can do typically do far more than just instantiate objects dynamically at runtime. Reflection allows you to enumerate over elements in the program itself. You can enumerate over types like classes and structs, get information about their methods like their names, parameters, return type etc. You can get information about classes themselves like if they are a subtype of another type, what interfaces they implement etc. You can also use it to create objects and call their methods. Reflections basically allows you to treat your program as a graph of objects from within itself.
I want to get C#-like reflection functionality in VB6.
Quote:
Originally Posted by
Niya
With reflection, you can do this at runtime like this:-
Code:
Private Sub DoItUsingReflection()
Dim asm = Assembly.GetExecutingAssembly
'Enumerate all types in the assembly
For Each t As Type In asm.GetTypes
'If the current type is a class or structure whose
'declaration is nested inside this Form class
If t.DeclaringType Is Me.GetType Then
'Look for a method called PrintValue on the class
Dim m = t.GetMethod("PrintValue")
'If the method exists then....
If m IsNot Nothing Then
'Create an instance of the class
Dim c As Object = Activator.CreateInstance(t)
'Call the PrintValue method on the object
m.Invoke(c, {})
End If
End If
Next
End Sub
The above does the same thing but at runtime instead. I told it to search for all types nested inside that Form class, instantiate them and call their PrintValue method. If I were to add other nested classes to the form with PrintValue methods afterwards and re-compile, I wouldn't have to change that function at all because the new class would be enumerated at runtime.
Yes, that's exactly what I want.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Niya
Oh I missed this one. CType actually throws an exception if it cannot perform a conversion. You're actually thinking of the
TryCast which works similarly to CType but returns Nothing if it fails to perform a conversion. TryCast only works where there is an inheritance relationship though so in that sense its more limited than CType.
Yes, I know that in VB.NET, there is a difference between CType, TryCast and DirectCast. But I want to implement the functionality of those three functions in my CType.
Quote:
Originally Posted by
The trick
You can get all the classes, types, enums for public project types in vb6. Each public project contains a tlb with types descriptions.
Could you give some specific examples? Thanks.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
jpbro
I was recently in a big debate about generics and reflection at the tB GitHub discussion board. Try as I might, I don't see generics & reflection as an improvement over the COM contractual approach.
Hi jpbro,
Your point makes sense. Generics make C# and VB.NET expressive, but generics also increase the complexity of the language. I've been looking for a solution that reduces the complexity of the language while still implementing most of the features of generics.
RC6.ArrayList provides another generic solution, for example:
Code:
Dim AL As cArrayList
Set AL = New_c.ArrayList(vbString)
That is to say, when variables are declared, there is no generic, but when creating an object, generic is used, but this solution also has some limitations, that is, we cannot use generics in function parameters and function return values, for example:
Code:
Public Function MyFunc(Params As List(Of String)) As List(Of MyClass)
End Function
Although I can partially implement some generic features by providing ListOfString, ListOfLong, ListOfObject, however, I cannot implement generics for all UDTs and classes such as ListOfMyClass1, ListOfMyClass2 ...
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
SearchingDataOnly
Your point makes sense. Generics make C# and VB.NET expressive, but generics also increase the complexity of the language. I've been looking for a solution that reduces the complexity of the language while still implementing most of the features of generics.
Dynamic typing achieves the same goals as generics while being much simpler to use and understand. However, you give up something big which is safety. Dynamically typed languages are error prone.
The thing one has to realize about choosing a programming language is that it's all about trade-offs. Even in cases where one language might be more powerful than another, you're still trading. You're never going to get one language that is better than another while still having everything the lesser language has. You're always going to give something up. For example, moving from VB6 to VB.Net you are giving up simplicity for flexibility. Moving from Assembly to C, you're giving up power for terseness.
-
Re: Generic type conversion function CType
Quote:
Originally Posted by
Niya
Dynamic typing achieves the same goals as generics while being much simpler to use and understand. However, you give up something big which is safety. Dynamically typed languages are error prone.
Maybe we can use a contract-class like dotnet's to reduce errors caused by dynamic typing, so as to achieve a balance between simplicity and safety.
TypeScript uses "dynamic interfaces" to improve the safety of dynamic typing. Of course, we can't seem to use "dynamic interfaces" in VB6