Is it possible to make a shared function generic? - vb.net

I can create classes that use generics, however I was wondering if it is possible to apply this to a shared method rather than rely on the caller casting to the correct type.
This is the method:
Public Shared Function DeserializeObject(ByVal serializedXml As String, ByVal givenType As System.Type) As Object
Dim serializer As New XmlSerializer(givenType)
Return serializer.Deserialize(New IO.StringReader(serializedXml))
End Function
I'm pretty sure this can't be done, but thought I'd check (if so Extra points will be awarded if someone can technically explain why the compiler can't do this).. Alternative suggestions would also be appreciated.

Public Shared Function StronglyTypedDeserializeObject(Of T)
(ByVal serializedXml As String) As T
Dim serializer As New XmlSerializer(GetType(T))
Return DirectCast(
serializer.Deserialize(New IO.StringReader(serializedXml))
, T)
End Function
Note that invocations will have to supply the type expected:
Dim foo As MyType
= Utility.StronglyTypedDeserializeObject(Of MyType)(sourceXml)

There's no reason you couldn't create a generic version of the function. e.g.:
Public Shared Function Deserialize(Of T)(ByVal serializedXml As String) As T
Dim serializer As New XmlSerializer(GetType(T))
Return DirectCast(serializer.Deserialize(New StringReader(serializedXml)), T)
End Function

The Following Example shows how you can do this. The esstential trick is remember that can you can use the GetType function a defined Generic for example GetType(MyObject(Of T))
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Test1 As New MyObject(Of String)
Dim Test2 As New MyObject(Of Integer)
Dim Data As String = ""
Test1.Data = "Hello World"
Test2.Data = 1010101
Data = Test1.SerializeObject
Test1 = MyObject(Of String).DeserializeObject(Data)
Debug.Print(Test1.Data)
Data = Test2.SerializeObject
Test2 = MyObject(Of Integer).DeserializeObject(Data)
Debug.Print(Test2.Data)
End Sub
End Class
Public Class MyObject(Of T)
Public Data As T
Public Sub New()
End Sub
Public Shared Function DeserializeObject(ByVal serializedXml As String) As MyObject(Of T)
Dim serializer As New XmlSerializer(GetType(MyObject(Of T)))
Return serializer.Deserialize(New IO.StringReader(serializedXml))
End Function
Public Function SerializeObject() As String
Dim serializer As New XmlSerializer(GetType(MyObject(Of T)))
Dim Result As New IO.StringWriter()
serializer.Serialize(Result, Me)
Return Result.ToString
End Function
End Class
If you don't like fully qualifying the type to get at the shared member then you can do something like this.
Public Class MyObjectString
Inherits MyObject(Of String)
End Class
Then you can call MyObjectString.Deserialize

Related

referencing VisualBasic methods

In VB, I want to assign a (shared) method to a variable
And I'm asking if there is some namespace operator like :: in C++ and Java
Class C
Public Shared Function m(a as Integer) as Integer
Return a * 2
End Function
End Class
Public Sub Main()
Dim localM As Func(Of Integer, Integer) = C.m
Console.WriteLine(localM(5))
End Sub
I know I could use function(a) C.m(a)
but that is not pretty
and I don't want to create a new function every time
and is also sensitive to argument changes
as for why would I do that
I'm actually passing it to function as an argument
(someFunction(3, C::m))
You can do this:
Sub Main()
Dim localM As Func(Of Integer, Integer) = AddressOf C.m
Console.writeline(localM(5))
End Sub
You can see it work here:
https://dotnetfiddle.net/Uxl0Y7
I didn't change anything in the sample class, except to complete the function so it returns a value (required for the function to compile) and to fix your lousy capitalization.
I'm actually passing it to function as an argument
In that case:
Sub Main()
Dim localM As Func(Of Integer, Integer) = AddressOf C.m
Foo(localM)
' Or
Foo(AddressOf C.m)
End Sub
Sub Foo(theMethod As Func(Of Integer, Integer))
Console.WriteLine(theMethod(5))
End Sub
In either case, the trick is to do two things:
Define either an Action (for a Sub) or Func (for a Function) that matches the signature of the target method. In this case, it's Func(Of Integer, Integer). Any method up to 8 arguments can be mapped this way, but you do have to know the signature in advance.
Use AddressOf to capture the reference to the method.
Is this what you are looking for?
Class C
Public Shared Add1 As Func(Of Integer, Integer) = Function(value As Integer)
Return value + 1
End Function
End Class
to use
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim foo As Integer = C.Add1(3)
Debug.WriteLine(foo)
End Sub
edit
Class C
Public Shared Add1 As Func(Of Integer, Integer) = Function(value As Integer)
Return value + 1
End Function
Public Shared DebugStr As Action(Of String, String) = Sub(first As String, second As String)
Debug.WriteLine("One-{0} Two-{1}",
first, second)
End Sub
End Class
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim foo As Integer = C.Add1(7)
Debug.WriteLine(foo)
C.DebugStr("Quick", "Fox")
End Sub

Create Custom Class Dynamically

I am working on a project where I need to create a multitude of custom classes to interact properly with an API (While I know there might be questions on why, and such, but the short is it has to be this way).
Is there a way to create a complete custom class dynamically on the fly? So instead of
class person
Private _Height
Property Height As Integer
Get
Return _Height
End Get
Set(value As Integer)
_Height = value
End Set
End Property
'Continue for all properties of person
I would like to be able to create a new object and through other input create this dynamically.
dim NewClass as object
dim NewProperty as property
NewProperty.name="Height"
NewProperty.datatype=string
NewClass.AddProperty(NewProperty)
Is this possible? It would save me a lot of time if it is.
I don't like late binding but there are options (I like my option strict on). Like using the DynamicObject or the ExpandoObject class. Your question is vague so I have no idea if it can work.
Sub Main()
Dim test As Object = New SampleDynamicClass()
test.SomeProperty = "123"
Console.WriteLine(test.SomeProperty)
Console.ReadLine()
End Sub
Public Class SampleDynamicClass
Inherits DynamicObject
Private _values As New Dictionary(Of String, String)
Public Sub New()
End Sub
Public Function GetPropertyValue(ByVal propertyName As String) As String
Return _values(propertyName)
End Function
Public Function SetPropertyValue(ByVal propertyName As String, ByVal value As Object) As Boolean
If _values.ContainsKey(propertyName) Then
_values(propertyName) = value.ToString()
Else
_values.Add(propertyName, value.ToString())
End If
Return True
End Function
Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder,
ByRef result As Object) As Boolean
result = GetPropertyValue(binder.Name)
Return If(result Is Nothing, False, True)
End Function
Public Overrides Function TryInvokeMember(ByVal binder As InvokeMemberBinder,
ByVal args() As Object,
ByRef result As Object) As Boolean
result = GetPropertyValue(binder.Name)
Return If(result Is Nothing, False, True)
End Function
Public Overrides Function TrySetMember(binder As SetMemberBinder, value As Object) As Boolean
Return SetPropertyValue(binder.Name, value)
End Function
Dim person = New With {Key .Height = 12}
Dim personTypes = New With {Key .Happy = 1, .Sad = 2}
Dim personsAndTypes = New With {Key .Person = person, .Type = personTypes}
The question is kind of vague, but if you have no need for other fields and methods, or reuse Anonymous Types

Pass-through methods vs. accessing nested objects directly

What I have is an object that contains a list of objects that each contain another list of objects that have properties and such.
Currently I use pass-through methods to be able to add to those nested objects, like in this extremely simplified example:
Public Class clsA
Private objB As List(Of clsB) = New List(Of clsB)
Public Sub New()
objB.Add(New clsB)
End Sub
Public Sub AddInt(ByVal BIndex As Int32, ByVal CIndex As Int32, ByVal Number As Int32)
objB(BIndex).AddInt(CIndex, Number)
End Sub
End Class
Public Class clsB
Private objC As List(Of clsC) = New List(Of clsC)
Public Sub New()
objC.Add(New clsC)
End Sub
Public Sub AddInt(ByVal CIndex As Int32, ByVal Number As Int32)
objC(CIndex).AddInt(Number)
End Sub
End Class
Public Class clsC
Private lstNum As List(Of Int32) = New List(Of Int32)
Public Sub AddInt(ByVal Number As Int32)
lstNum.Add(Number)
End Sub
End Class
It seems like proper coding standards would tell me this is correct compared to:
Public Class clsD
Public objE As List(Of clsE) = New List(Of clsE)
Public Sub New()
objE.Add(New clsE)
End Sub
End Class
Public Class clsE
Public objF As List(Of clsF) = New List(Of clsF)
Public Sub New()
objF.Add(New clsF)
End Sub
End Class
Public Class clsF
Public lstNum As List(Of Int32) = New List(Of Int32)
End Class
Are there some instances where either method would be acceptable? Or would the pass-through setup always be preferred?
Public Class Form1
Dim oA As clsA = New clsA
Dim oD As clsD = New clsD
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
oA.AddInt(0, 0, 3)
oD.objE(0).objF(0).lstNum.Add(3)
End Sub
End Class
Think about how it's done throughout the .NET Framework. The collection should be assigned to a private field and exposed via a public read-only property.
Public Class Thing
Private _stuff As New List(Of Thing)
Public ReadOnly Property Stuff() As List(Of Thing)
Get
Return _stuff
End Get
End Property
End Class
The caller can then access the collection directly to call its Add method, etc, but cannot assign a whole new collection. There are examples everywhere: Control.Controls, ListBox.Items, ComboBox.Items, ListView.Items, DataSet.Tables, DataSet.Relations, DataTable.Rows, Datatable.Columns, etc, etc, etc.

A class can only be a template for a single object not a template for a collection

I have a simple class List.vb which is the following:
Public Class List
Public fList As List(Of Integer)
Public Sub New()
fList = New List(Of Integer)
fList.Add(1)
fList.Add(2)
fList.Add(3)
fList.Add(4)
fList.Add(5)
End Sub
End Class
The Console application is using this class like the following:
Module Module1
Sub Main()
Dim fObject As List = New List
Dim cnt As Integer = 0
For Each x As Integer In fObject.fList
Console.WriteLine("hello; {0}", fObject.fList.Item(cnt).ToString())
cnt = cnt + 1
Next
Console.WriteLine("press [enter] to exit")
Console.Read()
End Sub
End Module
Can I change the class code so that List.vb is a list(of integer) type?
This would mean that in the Console code I could replace In fObject.fList with just In fObject?
Or am I barking up the wrong tree - should classes be single objects and lists should be collections of classes ?
Yes, you can do that. In order for an object to be compatible with For Each, it must have a GetEnumerator function:
Public Function GetEnumerator() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return New IntListEnum(fList)
End Function
The IntListEnum class must, in turn, implement IEnumerator, like this:
Public Class IntListEnum Implements IEnumerator
Private listInt As List(Of Integer)
Dim position As Integer = -1
Public Sub New(ByVal fList As List(Of Integer))
listInt = fList
End Sub
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
position = position + 1
Return (position < listInt.Count)
End Function
Public Sub Reset() Implements IEnumerator.Reset
position = -1
End Sub
Public ReadOnly Property Current() As Object Implements IEnumerator.Current
Get
Try
Return listInt(position)
Catch ex As IndexOutOfRangeException
Throw New InvalidOperationException()
End Try
End Get
End Property
End Class
Now you can make fList private, and iterate your List as follows:
For Each x As Integer In fObject
You can see a complete example here.
The answer that dasblinkenlight has provided is excellent, but if all you need is a list that of integers that is pre-populated, you can just inherit from List(Of Integer) and then have the class populate itself in the constructor:
Public Class List
Inherits List(Of Integer)
Public Sub New()
Add(1)
Add(2)
Add(3)
Add(4)
Add(5)
End Sub
End Class
When you inherit from List(Of Integer), your class automatically gets all of the functionality implemented by that type, so your class also becomes a list class that works the same way. Then, you can just use it like this:
Dim fObject As New List()
For Each x As Integer In fObject
Console.WriteLine("hello; {0}", x)
Next

Do local variables in shared method work like static variable in C?

Will the list in this shared method keep its state throughout the life of the method? Or will a new list be created every time this method is called?
Protected Shared Function newResxNodes(ByVal newName As String, ByVal newValue As String, Optional ByVal newComment As String = "") As List(Of ResXDataNode)
Dim newResxNodesList As List(Of ResXDataNode) = New List(Of ResXDataNode)
Dim newResxNode As ResXDataNode = New ResXDataNode(newName, newValue)
If newComment <> String.Empty Then
newResxNode.Comment = newComment
End If
newResxNodesList.Add(newResxNode)
Return newResxNodesList
End Function
No, It does not work like static variables in C. It will be a new list for every call. If you want to retain the list and list items, create a shared class field.
I've done a test and it returns 3 lines.
Module Module1
Class b
Public Sub New()
Console.WriteLine("New")
End Sub
End Class
Class a
Public Shared Sub Test()
Dim c As b = New b
End Sub
End Class
Sub Main()
a.Test()
a.Test()
a.Test()
Console.ReadLine()
End Sub
End Module