Extending a base class to a derived class - vb.net

We have two classes BasicRace & AdvancedRace.
AdvancedRace inherits from BasicRace
I have a BasicRace but i want to 'convert' it to the advanced class.
See code below as a example:
Module Module1
Sub Main()
Dim bRace As New BasicRace With {.CourseID = 1, .MeetingDate = "2013-05-01", .RaceNumber = 1}
Dim aRace As New AdvancedRace
' Upgrade bRace to AdvancedRace???????
End Sub
End Module
Public Class BasicRace
Public Property MeetingDate As Date
Public Property CourseID As Integer
Public Property RaceNumber As Integer
End Class
Public Class AdvancedRace
Inherits BasicRace
Public Property RaceTitle As String
End Class
Any help would be great - I'm starting to think it can not be done unless i write a function to convert a basicRace to AdvancedRace going through each property one by one?

You can't "convert" from a base class to a subclass as such (you can't change the type of an existing object), but you can create new instance of the subclass that copies the properties from your base class.
Typical ways of implementing this for your classes might be:
a constructor in AdvancedRace that takes a BasicRace parameter and copies properties from it
a static method in AdvancedRace that takes a BasicRace parameter, creates the new object with copied properties, and then returns it
It's worth noting that this will result in two completely separate objects (one of each type) that aren't linked at all - changes in your AdvancedRace object won't be reflected in the BasicRace or vice-versa.

Related

Passing a derived class containing an overloaded property as a parameter

I have two objects, one is a base class and the other is a derived class which inherits the base class. One of the properties on the derived class overloads a property on the base class. Now, I want to perform some calculations on both of these objects one by one by passing them as a parameter into a function. The problem is, if I define the parameter of this function as a base class, then when passing the derived class, the value of the overloaded property gets lost!
The reason I'm using a derived class is to temporarily add more properties/modify the existing properties of the base class to perform additional calculations, in order to reuse the base class.
I've tried 4 different functions, but none of them are any good. They either don't work correctly, or there is duplicate code, which I need to avoid because there will be a lot more code later. Below is the pseudo code.
Defining the classes:
Class BaseClass
Public Property Name As String
Public Property Value As Integer
End Class
Class DerivedClass
Inherits BaseClass
Overloads Property Value As Double
End Class
Initializing:
Dim MyBaseObject As New BaseClass()
MyBaseObject.Name = NameOf(MyBaseObject)
MyBaseObject.Value = 5
Dim MyDerivedObject As New DerivedClass
MyDerivedObject.Name = NameOf(MyDerivedObject)
MyDerivedObject.Value = 5.3
Calling the functions:
ProcessClass1(MyBaseObject)
ProcessClass1(MyDerivedObject)
ProcessClass2(MyBaseObject)
ProcessClass2(MyDerivedObject)
ProcessClass3(MyBaseObject)
ProcessClass3(MyDerivedObject)
ProcessClass4(MyBaseObject)
ProcessClass4(MyDerivedObject)
The functions:
Sub ProcessClass1(inClass As Object) 'functions correctly, but no intellisense
Console.WriteLine(inClass.Name & " " & inClass.Value)
End Sub
Sub ProcessClass2(inClass As BaseClass) 'does not function correctly, but has intellisense
Console.WriteLine(inClass.Name & " " & inClass.Value) 'Value displays 0 when passing MyDerivedObject, it should be 5.3!
End Sub
Sub ProcessClass3(inClass As Object) 'functions correctly, has intellisense, but need to write code for all possible derived types in advance
If inClass.GetType = GetType(BaseClass) Then
Dim inBaseClass As BaseClass = inClass
Console.WriteLine(inBaseClass.Name & " " & inBaseClass.Value)
End If
If inClass.GetType = GetType(DerivedClass) Then
Dim inDerivedClass As DerivedClass = inClass
Console.WriteLine(inDerivedClass.Name & " " & inDerivedClass.Value)
End If
End Sub
Sub ProcessClass4(inClass As BaseClass) 'method overloading: functions correctly, has intellisense, but need to write a duplicate method for every derived type
Console.WriteLine(inClass.Name & " " & inClass.Value)
End Sub
Sub ProcessClass4(inClass As DerivedClass) 'method overloading: functions correctly, has intellisense, but need to write a duplicate method for every derived type
Console.WriteLine(inClass.Name & " " & inClass.Value)
End Sub
Extra: Generics
I don't see any advantage with generics, the below snipped runs into the same problem as ProcessClass2:
Dim MyProcessGenericObject As New ProcessGenericClass(Of BaseClass)
MyProcessGenericObject.processNewItem(MyBaseObject)
MyProcessGenericObject.processNewItem(MyDerivedObject)
Public Class ProcessGenericClass(Of T As BaseClass)
Public Sub processNewItem(ByVal newItem As T)
Console.WriteLine(newItem.Name & " " & newItem.Value) 'Value displays 0 when passing MyDerivedObject!
End Sub
End Class
Of these 4 functions, ProcessClass1 is the most elegant with the least amount of code, but there is no intellisense on inClass which makes it impossible to maintain.
What I need is no duplication of code, intellisense, a method which can take derived classes inherited from the same base class, and without losing the data contained in any overloaded properties. What would be the best way to achieve this? Thanks.
What you have put forward will not work with the instance you pass around being the base class. That instance's value property will always be an integer unless you are able to cast the instance to the appropriate derived class (and that (double)int cast is where you have lost precision).
But a combination of some of these generics may help. Note, the base class will not hold an integer, rather an Object.
Public Class BaseClass
Public Property Name As String
Public Property Value As Object
End Class
Public Class BaseClass(Of T)
Inherits BaseClass
Public Overloads Property Value As T
Get
Return CType(MyBase.Value, T)
End Get
Set(value As T)
MyBase.Value = value
End Set
End Property
End Class
Class DerivedClassDouble
Inherits BaseClass(Of Double)
End Class
Class DerivedClassInteger
Inherits BaseClass(Of Integer)
End Class
The process method
Sub ProcessClass(inClass As BaseClass)
Console.WriteLine($"{inClass.Name} {inClass.Value}")
End Sub
Some options for instantiation
Dim [myBase] As New BaseClass()
[myBase].Name = NameOf([myBase])
[myBase].Value = 5
Dim myBaseInteger As New BaseClass(Of Integer)
myBaseInteger.Name = NameOf(myBaseInteger)
myBaseInteger.Value = 5
Dim myDerivedInteger As New DerivedClassInteger
myDerivedInteger.Name = NameOf(myDerivedInteger)
myDerivedInteger.Value = 5
Dim myBaseDouble As New BaseClass(Of Double)
myBaseDouble.Name = NameOf(myBaseDouble)
myBaseDouble.Value = 5.3
Dim myDerivedDouble As New DerivedClassDouble
myDerivedDouble.Name = NameOf(myDerivedDouble)
myDerivedDouble.Value = 5.3
ProcessClass([myBase])
ProcessClass(myBaseInteger)
ProcessClass(myDerivedInteger)
ProcessClass(myBaseDouble)
ProcessClass(myDerivedDouble)
Console.ReadLine()
myBase 5
myBaseInteger 5
myDerivedInteger 5
myBaseDouble 5.3
myDerivedDouble 5.3
I think the closest to your implementation would be to use [myBase] and myDerivedDouble instances. Then changing the generic base class to Public MustInherit Class BaseClass(Of T) would make the intent clearer.
Hopefully last edit, sorry for the long-winded answer.
You can just change your original classes to have an object in the base class, and use the property implementation I laid out, and that seems to get the job done without any generics. Again, it may or may not work in your exact implementation
Sub Main()
Dim MyBaseObject As New BaseClass()
MyBaseObject.Name = NameOf(MyBaseObject)
MyBaseObject.Value = 5
Dim MyDerivedObject As New DerivedClass
MyDerivedObject.Name = NameOf(MyDerivedObject)
MyDerivedObject.Value = 5.3
ProcessClass(MyBaseObject)
ProcessClass(MyDerivedObject)
Console.ReadLine()
End Sub
Sub ProcessClass(inClass As BaseClass)
Console.WriteLine($"{inClass.Name} {inClass.Value}")
End Sub
Public Class BaseClass
Public Property Name As String
Public Property Value As Object
End Class
Public Class DerivedClass
Inherits BaseClass
Overloads Property Value As Double
Get
Return CDbl(MyBase.Value)
End Get
Set(value As Double)
MyBase.Value = value
End Set
End Property
End Class
MyBaseObject 5
MyDerivedObject 5.3

How to create a list of multiple object types and preserve methods/properties?

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.

How to get properties from nested object using reflection and recursion?

I have a set of classes, whose properties have data annotations on them. Some of these class properties are of primitive types (and by primitive, I also mean types such as string, double, datetime etc), while others are properties of a custom type.
I would like to be able to iterate through the properties of a class and the properties of the nested objects and pull out the attributes of each property. I’ve played around with reflection and my code works fine, if the class under consideration has only one property of a custom type.
However when a class has multiple properties of a custom type and each of those properties have other custom types, I am completely lost on how I’d keep track of the objects/properties that have already been visited.
This is where I have got so far. I have seen a lot of examples on the forum, but they all have a simple nested class, where there is a maximum of one custom type per class.
Below is a sample of what I am trying to get done:
Public Class Claim
<Required()>
<StringLength(5)>
Public Property ClaimNumber As String
<Required()>
Public Property Patient As Patient
<Required()>
Public Property Invoice As Invoice
End Class
Public Class Patient
<Required()>
<StringLength(5)>
Public Property MedicareNumber As String
<Required()>
Public Property Name As String
<Required()>
Public Property Address As Address
End Class
Public Class Address
Public Property Suburb As String
Public Property City As String
End Class
Public Class Invoice
<Required()>
Public Property InvoiceNumber As String
<Required()>
Public Property Procedure As String
End Class
Public Shared Function Validate(ByVal ObjectToValidate As Object) As List(Of String)
Dim ErrorList As New List(Of String)
If ObjectToValidate IsNot Nothing Then
Dim Properties() As PropertyInfo = ObjectToValidate.GetType().GetProperties()
For Each ClassProperty As PropertyInfo In Properties
Select Case ClassProperty.PropertyType.FullName.Split(".")(0)
Case "System"
Dim attributes() As ValidationAttribute = ClassProperty.GetCustomAttributes(GetType(ValidationAttribute), False)
For Each Attribute As ValidationAttribute In attributes
If Not Attribute.IsValid(ClassProperty.GetValue(ObjectToValidate, Nothing)) Then
ErrorList.Add("Attribute Error Message")
End If
Next
Case Else
Validate(ClassProperty.GetValue(ObjectToValidate, Nothing))
**** ‘At this point I need a mechanism to keep track of the parent of ClassProperty and also mark ClassProperty as visited, so that I am able to iterate through the other properties of the parent (ObjectToValidate), without revisiting ClassProperty again.**
End Select
Next
End If
Return Nothing
End Function
The most straightforward (and probably easiest) way to approach this is to keep a Dictionary of class property attributes keyed by class name.
If I were approaching this, I would probably create a class to hold the property attributes:
Public Class PropertyAttribute
Public PropertyName As String
Public PropertyTypeName As String
Public Required As Boolean
Public StringLength As Integer
End Class
Then create a class to hold information about each class' properties:
Public Class ClassAttributes
Public ClassName As String
' You could also use a dictionary here to key properties by name
Public PropertyAttributes As New List(Of PropertyAttribute)
End Class
Finally, create a dictionary of ClassAttributes to keep track of which custom classes you have already processed:
Public ProcessedClasses As New Dictonary(Of String, ClassAttributes)
The key for the dictionary is the classname.
When you are processing the attributes through reflection, if the property type is custom, check the dictionary for the existence of the class. If it is there, you don't have to process it.
If it is not there, add a new instance to the dictionary immediately (so that nested objects of the same type are safely handled) and then process the attributes of the class.

Deep Copy of an Object

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.

Base Classes on Proxy Objects

I have a webservice that is wrapped up by a data access object and is accessed by many different UI controls.
The proxy objects look something like this:
Public Class WebProxyObject1
' Common properties, there are about 10 of these
Public Name As String
Public Address As String
' Specialized properties there are about 20 of these
Public count As Integer
End Class
The DAL layers look something like this:
Public Class DataAccessObject
Implements IDataAccessObject
' These are called in MANY, MANY, MANY locations
Public Function GetObject(ByVal name As String) As WebProxyObject1 Implements IDataAccessObject.GetObject
' Makes call to a webservice
Return New WebProxyObject1
End Function
Public Function ListObjects() As System.Collections.Generic.List(Of WebProxyObject1) Implements IDataAccessObject.ListObjects
' Makes call to a webservice
Dim list As New List(Of WebProxyObject1)
Return list
End Function
End Class
Now, I need to add a 2nd webservice to the mix. The goal is to reuse the UI controls that are currently coded to use the Proxy Objects that come from the first webservice. There are about 10 common properties and about 20 that are different. To add the 2nd webservice, I'd create a 2nd DAL object that implements the same interface. The problem is that it currently returns the proxies from the first webservice.
My thought on how to solve this is to extract an interface from each of the proxy objects and mash them together. Then implement the new interface on both proxy objects. That will create a huge class/interface where some properties aren't used. Then have the DAL return the interface.
The problem that I'm facing isn't a real bug or an issue, but extracting the 2 interfaces and smashing them together just feels kind of wrong. I think it would technically work, but it kind of smells. Is there a better idea?
The resulting interface would look like this:
Public Interface IProxyObject
' Common
Property Name() As String
Property Address() As String
' Specialized
Property Count() As Integer
Property Foo() As Integer
End Interface
Create a base class for your WebProxyObjects to inherit from.
Public MustInherit Class WebProxyObjectBase
' Common properties
Public Property Name As String
Public Property Address As String
End Class
Next create your two WebProxyObjects:
Public Class WebProxyObject1
Inherits From WebProxyObjectBase
' Specialized properties
Public Property count As Integer
End Class
Public Class WebProxyObject2
Inherits From WebProxyObjectBase
' Specialized properties
Public Property foo As Integer
End Class
Next have your DAL return the Base Class:
Public Class DataAccessObject
Implements IDataAccessObject
' These are called in MANY, MANY, MANY locations
Public Function GetObject(ByVal name As String) As WebProxyObjectBase Implements IDataAccessObject.GetObject
' Makes call to a webservice
Return New WebProxyObjectBase
End Function
Public Function ListObjects() As System.Collections.Generic.List(Of WebProxyObjectBase) Implements IDataAccessObject.ListObjects
' Makes call to a webservice
Dim list As New List(Of WebProxyObjectBase)
Return list
End Function
End Class
Then when calling your DataAccessObject you'll be able to ctype the return to the proper class:
Dim DAO as New DataAccessObject
Dim Pxy1 as WebProxyObject1 = TryCast(DAO.GetObject("BOB"), WebProxyObject1)
If Pxy1 IsNot Nothing Then
'Do stuff with proxy
End If