I have a class we will call MyClass. Inside I have some deserialization code that I want to be able to call from within the object. Is there a cleaner way to assign the values associated with the class from within the class.
Here is what I am doing now
Public Class MyClass
Public Property Prop1 as New String(String.empty)
Public Property Prop2 as New String(String.empty)
Public Property Prop3 as Boolean = False
Public Sub LoadXML(ByVal XMLText as String)
Dim MyTemp as New MyClass
MyTemp = CType(DeSerialize(XMLText, MyTemp.Type), MyClass) 'this returns an object
Me.Prop1 = MyTemp.Prop1
Me.Prop2 = MyTemp.Prop2
Me.Prop3 = MyTemp.Prop3
End Sub
End Class
I cannot assign the result of MyTemp to Me (which ostensibly represent the same object type) but I can assign all of the properties of MyTemp to the properties of Me. Since my actual class is much more complicated (I used primitives in the example - but in reality is a pretty large class with properties that are many other classes) I wondered if there is a better way to assign the value of MyTemp to the instance of the class.
You probably want to create a Shared function that will return an instance of MyClass in lieu of a constructor.
Public Shared Function LoadXML(ByVal XMLText As String) As MyClass1
Dim MyTemp As New MyClass1
MyTemp = CType(DeSerialize(XMLText, MyTemp.GetType()), MyClass1)
Return MyTemp
End Function
You can run this to create the initial instance of the class, so instead of calling new you would call MyClass.LoadXML(xmlString).
You would probably be a lot better off to declare LoadXML as a shared function that just returned the deserialized object. That way, instead of
dim myClassInstance = new MyClass();
myClassInstance.LoadXML(...)
You would do
dim myClassInstance = MyClass.LoadXML(...);
And you wouldn't need to do all that property copying.
(sorry, VB is rusty)
Related
I have a program that de-serializes stuff from an xml file and does all kinds of fancy things with it. I have 2 arrays in the XML file, one called variables and one called lookupTables. I also have 2 classes, variable and lookupTable. Both of those classes inherit from a class called definition. definition is inherit only and has one method that must be inherited, evaluate. Here is the code:
Definition
Public MustInherit Class Definition
Public Sub New()
End Sub
<XmlAttribute("name")> _
Public Property name As String
Public MustOverride Function evaluate(variables As Dictionary(Of String, Double)) As Double
End Class
Variable
<XmlRoot("variable")> _
Public Class Variable
Inherits Definition
<XmlAttribute("value")> _
Public Property value As String
Public Overrides Function evaluate(variables As Dictionary(Of String, Double)) As Double
Dim calculator As CalculationEngine = New CalculationEngine()
Return calculator.Calculate(value, variables)
End Function
End Class
LookupTable
<XmlRoot("lookupTable")> _
Public Class LookupTable
Inherits Definition
Public Sub New()
End Sub
<XmlElement("data")> _
Public Property data As Integer()()
Public Overrides Function evaluate(variables As Dictionary(Of String, Double)) As Double
Return True
End Function
End Class
My question is (hopefully) very simple. How can I create a list of Defintions (so a list containing both Variables and LookupTables) without loosing their individual methods and properties. All I will need to use this list for is calling evaluate.
I thought I could just create a List(Of Definition) since both Variable and LookupTable are guaranteed to implement evaluate() but as I read, it seems that typecasting both of the lists would strip them of their own innards and keep onluy what is common with Definition. What can I do here?
Since both your objects inherit from definition, you could create a list of Definition items then when you need to access specific methods, you cast them to their proper type using directCast to their specific type. To determine the type, you can use
If you had multiple variables types not inheriting from the same base, you could create a list of objects and apply the same idea.
'List of definition item
Private _List As New List(Of Definition)
'When you want to use specific method, you can cast items back to their types.
For Each Item As Definition In _List
Select Case Item.GetType
Case GetType(LookupTables)
Dim Table As LookupTables = DirectCast(Item, LookupTables)
Table.msg() 'Method in my LookupTables class only.
Case GetType(Variables)
Dim Variable As Variables = DirectCast(Item, Variables)
Variable.WriteToConsole() 'Method found in Variables class only.
End Select
Next
As for casting,
you can cast your LookupType to definition and vice-versa to use their respective methods as needed.
The simple answer was to use an ArrayList.
I have an ArrayList defined in Class A. Then I want to build this array in Class B and use it in Class A.
I defined the ArrayList as:
Public arrayList As ArrayList
Then, in Class B I do:
Dim trLogkEmpty As New A
'Loop with strEspece definition
trLogkEmpty.arrayList.Add(strEspece)
'End Loop
The program throws me this error:
NullReferenceException
I don't know why, because strEspece has never become null (I tested it). I don't know if there is another reason.
Also, when I loop through the arrayList elements in Class A, I get again NullReferenceException. This is the loop code:
For Each logkNull In Me.arrayElemWithLogkEmpty
Console.WriteLine(logkNull)
Next
I don't know what happens with the first exception, but the code runs "correctly". In the second exception I guess that is something like I'm loosing the elements values of the array. I don't know how to solve it...any help? I accept different ways to solve it!
You are making two of the same mistake. A NullReferenceException means that you are attempting to access a property or method on an object that hasn't been instantiated yet. You are attempting to access both A and A.arrayList without first creating new instances of them.
So, instead of just:
trLogkEmpty.arrayList.Add(strEspece)
You should have:
Dim trLogkEmpty As New A()
trLogkEmpty.arrayList = New ArrayList()
trLogkEmpty.arrayList.Add(strEspece)
However, I must insist that you avoid ArrayList, and also that you avoid instantiating a public member of a class from outside that class. I would suggest using a strongly-typed collection class such as List(Of T), and having a read-only property in A's take care of its instantiation and visibility so the collection (not its contents) can't be modified outside of A:
Public Class A
Private _myList As IList(Of String)
Public ReadOnly Property MyList As IList(Of String)
Get
If _myList Is Nothing Then
_myList = New List(Of String)
End If
Return _myList
End Get
End Property
End Class
And now you have:
Dim trLogkEmpty As New A()
trLogkEmpty.MyList.Add(strEspece)
You're probably going to need to keep your instance of A around, so class B should probably look somewhat like:
Public Class B
Private _a As A
Public Sub New()
_a = New A()
End Sub
' ... your methods that use _a.MyList
End Class
I got it finally. When I initialised the array in class 'A' I forget to create an instance of ArrayList class, specifically, I forget to put New:
Public arrayElemWithLogkEmpty As New ArrayList
So, partly, #Blackwood was right!
Thank you all and forgive me for my basic knowledge about vb.net.
I'm trying to teach myself reflection and have been googling but I can't wrap my head around it entirely. I created a class called DataClass which contains a method called GetClassFromDB as you can see below, which will be inherited from multiple classes.
What I am attempting to do is have my dataclass read the TableName property that is defined within objResults. Once I pull in the tablename from objResults I would query the SQL database for a dataset. Once I have the dataset I would create a new object of the same TYPE inheriting this class (Which will be different types) and populate it from the dataset. Once I have the newly populated class I will return it for use.
I believe I have gotten most of the way there properly (Please correct me if there is a better way), but my real question is this. How can I create a new class of the type thats deriving that class from that string name that I getting in my code, or the type. I would want to have all the accessible properties from objResults available.
Namespace MyApp
Public Class DataClass
Private _TableName As String
Private _Name As String
Overridable ReadOnly Property TableName As String
Get
Return _TableName
End Get
End Property
Public Overloads Function GetClassFromDB() As Object
Try
Dim BaseObject As New Object
'Get the object name
Dim objName As String = MyBase.GetType().Name
'Gets the type thats calling this method
Dim objDerived As Type = MyBase.GetType()
'Get the property info to request the tablename from the derived class
Dim TableName As PropertyInfo = objDerived.GetProperty("TableName")
Dim TableNameString As String = TableName.GetValue(Me, Nothing).ToString
'Once I get the table name from objResults I can perform the SQL
Dim QueryResults as DataSet = SQLiteCLass.Query("Select * FROM TableNameString")
'Once I get data from the SQL I want to create a new object of the type deriving this method.
'In this example is objResults
Dim NewObject as objDerived
'Now I can fill my new object with the results and return it as an object
'THIS IS MY QUESTION - How can I create a new object of the TYPE that I receive from Reflection
Return False
Catch ex As Exception
Return False
End Try
End Function
End Class
End Namespace
and this is a sample class that would inherit my dataclass.
Public Class objResults
Inherits MyApp.DataClass
Private _GameID As Guid
Public Property GameID As Guid
Get
Return _GameID
End Get
Set(ByVal value As Guid)
_GameID = value
End Set
End Property
Public Overrides ReadOnly Property TableName As String
Get
Return "This is my tablename"
End Get
End Property
End Class
and this is how I would use this in code.
Dim objResult as New objResults
Dim TodaysResult as objResultsCollection
TodaysResult = objResult.GetClassFromDB()
Can I please have some help to perform a deep copy of an object.
Here is my code:
Option Explicit On
Option Strict On
<Serializable> Public Class [Class]
Private _Name As String
Private _ListOfFields As New List(Of Field)
Public Property Name As String
Get
Return _Name
End Get
Set(value As String)
_Name = value
End Set
End Property
Public Property ListOfFields As List(Of Field)
Get
Return _ListOfFields
End Get
Set(value As List(Of Field))
_ListOfFields = value
End Set
End Property
Public Function Clone() As [Class]
Return DirectCast(Me.MemberwiseClone, [Class])
End Function
End Class
Field is a Class that I have written myself as well.
What do I need to modify for the Clone() Function to return a deep copy?
You can create a clone of any class by calling this helper function:
Function DeepClone(Of T)(ByRef orig As T) As T
' Don't serialize a null object, simply return the default for that object
If (Object.ReferenceEquals(orig, Nothing)) Then Return Nothing
Dim formatter As New BinaryFormatter()
Dim stream As New MemoryStream()
formatter.Serialize(stream, orig)
stream.Seek(0, SeekOrigin.Begin)
Return CType(formatter.Deserialize(stream), T)
End Function
This works by serializing all the information from your class into a portable object and then rewriting it in order to sever any reference pointers.
Note: The passed in class and any other classes it exposes as properties must be marked <Serializable()> in order to use BinaryFormatter.Serialize
If you want to make your own class expose the clonable method itself, you can add the method and implement the ICloneable interface like this:
<Serializable()>
Public Class MyClass : Implements ICloneable
'NOTE - The Account class must also be Serializable
Public Property PersonAccount as Account
Public Property FirstName As String
Function Clone(ByRef orig As MyClass) As MyClass Implements ICloneable.Clone
' Don't serialize a null object, simply return the default for that object
If (Object.ReferenceEquals(orig, Nothing)) Then Return Nothing
Dim formatter As New BinaryFormatter()
Dim stream As New MemoryStream()
formatter.Serialize(stream, orig)
stream.Seek(0, SeekOrigin.Begin)
Return CType(formatter.Deserialize(stream), T)
End Function
End Class
Note: Be aware ICloneable comes with it's share of controversies as it does not indicate to the caller if it is performing a deep or shallow clone. In reality, you don't need the interface to be able to add the method to your class.
(As an aside, I probably would name your class something other than "Class").
If you wanted to do it all by hand you would need to follow steps like:
Ensure that your Field class also implements a deep copy Clone() method. If you haven't done this already, then this would likely involve its Clone() method creating a new object of type Field and then populating each of its properties based on the current object. If your Field class has properties which are other classes/complex types (e.g. classes you have created yourself) then they should also implement Clone() and you should call Clone() on them to create new deep copies
In your Clone() method for the class you would create a new object of type [Class], e.g. by calling its constructor
Set the Name property of the new object to the Name property of your current object
Create a new List(Of Field), let's call it listA for the sake of example
Iterate over your current list and assign a clone of each list item to listA. For example:
For Each item in _ListOfFields
listA.Add(item.Clone())
End
After that you can assign your new list (listA) to the object you have created in the Clone() method
There is an alternative (probably better) by-hand approach that is in VB.NET described here.
If you wanted to cheat a bit then you could just serialize your existing object and then deserialize it into a new object like the technique here
I would say the serialize then deserialize technique is the "easiest" one.
Is it possible using Reflection or some other method to obtain a reference to a specific class instance from the name of that class instance?
For example the framework for the applications i develop heavily uses public class instances such as:
Public bMyreference as MyReference = new MyReference
Then throughout the application bMyReference is used by custom controls and code.
One of the properties of the custom controls is the "FieldName" which references a Property in these class instances (bMyReference.MyField) as a string.
What i would like to be able to do is analyze this string "bMyReference.MyField" and then refer back to the actual Instance/Property.
In VB6 I would use an EVAL or something simular to convert the string to an actual object but this obviously doesn't work in VB.net
What I'm picturing is something like this
Dim FieldName as String = MyControl.FieldName ' sets FielName to bMyReference.MyField
Dim FieldObject() as String = FieldName.Split(".") ' Split into the Object / Property
Dim myInstance as Object = ......... ' Obtain a reference to the Instance and set as myInstance
Dim myProperty = myInstance.GetType().GetProperty(FieldObject(1))
I don´t know if I´ve understood you well, but my answer is yes, you can do it by reflection. You´ll need to import System.Reflection namespace.
Here is an example:
' Note that I´m in namespace ConsoleApplication1
Dim NameOfMyClass As String = "ConsoleApplication1.MyClassA"
Dim NameOfMyPropertyInMyClass As String = "MyFieldInClassA"
' Note that you are getting a NEW instance of MyClassA
Dim MyInstance As Object = Activator.CreateInstance(Type.GetType(NameOfMyClass))
' A PropertyInfo object will give you access to the value of your desired field
Dim MyProperty As PropertyInfo = MyInstance.GetType().GetProperty(NameOfMyPropertyInMyClass)
Once you have MyProperty, uou can get the value of your property, just like this:
MyProperty.GetValue(MyInstance, Nothing)
Passing to the method the instace of what you want to know the value.
Tell me if this resolve your question, please :-)
EDIT
This would be ClassA.vb
Public Class MyClassA
Private _myFieldInClassA As String
Public Property MyFieldInClassA() As String
Get
Return _myFieldInClassA
End Get
Set(ByVal value As String)
_myFieldInClassA = value
End Set
End Property
End Class