I would like to declare class level variables for a bunch of child classes similar to this in python: Static class variables in Python. I've learned that the shared attributed is probably what I want to use, but I'm not quite sure where to use it.
Here's my situation: I am making a bunch of different character types for a game (100+). All the character types have the same set of properties, so I've created a base class that includes all of the properties and the required internal variables. Each character type has a set of immutable properties that are exactly the same for every instance of the given character type, but differ between character types. I would like to contain the immutable character type properties as class "variables" in child classes (one child class per character type), like in the answer to the python example above.
The key is I would like to reference some properties WITHOUT having to create an instance of the sub class.
My problem is I can't figure out what needs to be shared to properly do this or how my code needs to be organized to make this happen. I tried sharing the properties which finally allowed me to get to the properties from the child class and not just an instance of the child, but then it wouldn't return the property I assigned in the child class using Shared or Shared Shadows. I would like to keep the properties in the base class so that I don't have to copy and paste the same properties for every child class.
Here is some samples of my code:
Public MustInherit Class Char
'IMMUTABLE INFORMATION
' CharType Traits
Shared _ID As Integer
Shared _Species As String
Public Shared ReadOnly Property Species As String
Get
Return _Species
End Get
End Property
End Class
Public Class CharCat
Inherits Char
Shared Shadows _Species = "Cat"
End Class
The goal is to be able to type CharCat.Species and have it return "Cat". I know this example is redundant with the class name, but for other properties it's not.
You can make the BaseClass Species Property Overridable to achieve what you're looking for.
Public MustInherit Class [Char]
Private _ID As Integer
Private _Species As String
Public Overridable ReadOnly Property Species As String
Get
Return _Species
End Get
End Property
End Class
Public Class CharCat
Inherits Char
Public OverLoads Shared ReadOnly Property Species As String
Get
Return "Cat"
End Get
End Property
End Class
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
MsgBox(CharCat.Species)
End Sub
Using NoAlias's response as a spring board in collaboration with more research and SO questions, I've found an complete answer. The base class does NOT need Overridable considering that, since the child class uses Shared, it CAN'T have a Overrides property. Overloads reportedly does nothing to a property according to Hans Passant so we don't need that either.
Now the code works with these two changes but there's a warning flag that says the property should be declared as "Overloads" which isn't actually the proper solution. To properly get rid of the warning, Shadows should be declared. The code worked without declaring shadows because Shadowing is the default behavior for a child property, but in order to get rid of the warning VB throws, you need to declare it explicitly.
The code ends up looking like this:
Public MustInherit Class [Char]
Private _ID As Integer
Private _Species As String
Public ReadOnly Property Species As String
Get
Return _Species
End Get
End Property
End Class
Public Class CharCat
Inherits Char
Public Shared Shadows ReadOnly Property Species As String
Get
Return "Cat"
End Get
End Property
End Class
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
MsgBox(CharCat.Species)
End Sub
It's important to note that we now have to deal with the Shadows behavior. If a child class is called through the parent class, you'll get parent class behavior for the property. For me, since I couldn't use Overrides because I'm using Shared, I decided the best way to avoid the parent class behavior was to just get ride of the parent class property since it wasn't holding any important information anyways. You could try be super careful not to call a child through the parent in your code to avoid the parent behavior as well.
Related
I am currently writing an API which is taking objects from a web service handling it in VB.net using the Newsoft JSON .NET library.
I'm deserializing a JSON array called Vehicles into a list of vehicles.
Here is the important code snippets:
Public Class VehicleList
Public Vehicles() As Vehicle
End Class
Public Class Vehicle
Public Property licence_plate_number As String
End Class
Here we have a web client that grabs the json and put it into the objects.
Public Class dcVehicles
Private Property _Vehicles As VehicleList
Public ReadOnly Property Vehicle As Vehicle()
Get
Return _Vehicles.Vehicles
End Get
End Property
Public Sub Refresh()
_Vehicles = JsonConvert.DeserializeObject(Of VehicleList)(wcReply, jsSettings)
End Sub
End Class
There's slightly more to it (cut it down).
So everything is working as intended, json net is creating an array of vehicles.
I'm trying to achieve this with the properties in the vehicle class as private and read only, the applications using the api should not be able to set these.
The problem is I've tried changing the public property in the vehicle class to keep the property private and allow readonly as below:
Public Class Vehicle
Friend Property licence_plate_number As String
Public ReadOnly Property RegistrationNumber As String
Get
Return licence_plate_number
End Get
End Property
End Class
The problem I then get is that JSON.net is unable to populate the vehicles. All 3 classes are in the same namespace.
So I tried licence_plate_number with the Friend/private access level modifier but still Json net is unable to populate the object.
The only way is keeping it as Public.
Does anyone have an idea for a work-around? Or is am I missing something simple?
Thanks
If you just want to serialize a Private or Friend property with Json.NET, mark it with <JsonProperty> and mark the public readonly property you don't want to serialize with <JsonIgnore>:
Public Class Vehicle
<JsonProperty> _
Friend Property licence_plate_number As String
<JsonIgnore> _
Public ReadOnly Property RegistrationNumber As String
Get
Return licence_plate_number
End Get
End Property
End Class
Demo fiddle.
But if you really want read-only value semantics for the licence_plate_number property so that it cannot be set after construction, you can do this by replacing the default constructor with a single parameterized constructor and matching the constructor argument names to the JSON property names, like so:
Public Class Vehicle
Private Readonly licence_plate_number As String
Public Sub New(ByVal licence_plate_number as String)
Me.licence_plate_number = licence_plate_number
End Sub
<JsonProperty("licence_plate_number")> _
Public ReadOnly Property RegistrationNumber As String
Get
Return licence_plate_number
End Get
End Property
End Class
When there is a single public constructor that is parameterized, Json.NET will call it, matching the constructor arguments to the JSON properties by name using reflection and using default values for missing properties. Matching by name is case-insensitive, unless there are multiple matches that differ only in case, in which case the match becomes case sensitive.
Demo fiddle.
If your class has multiple public constructors, mark the one to use with <JsonConstructor>.
I'm looking for suggestions on how to create a custom collection class in VB.Net, to contain instances of a custom object class. There is so much information on the topic that I'm not sure which direction to go in, and custom collections are new to me.
The collection needs are as follows:
collection should be read-only, it and its objects cannot be modified.
collection will always be a fixed size.
because of above two items, add, remove, count, clear, etc aren't needed.
will create & manage all object instances itself at instantiation.
should have a default property like "Item". (?)
should be enumerable, so For-Each can be used on it.
Here is a simplified outline of what I've pieced together so far:
Class Bay
Private ID As Integer
Private p_String As String
Private p_Aisle As Integer
...etc...
...getters, setters, & subs...
End Class
Class Bays
Inherits ...something...
Implements ....somethingelse... (?)
Public ReadOnly MyCollection(5094) as SomeCollectionType (Of Bay)
Private LastUpdate As Date
Private SystemStatus as Integer
Public Sub New()
...instantiate all objects in collection...
End Sub
...properties...
End Class
This is the sort of thing you can do:
Public Class Thing
'...
End Class
Public Class ReadOnlyThingCollection
Inherits ReadOnlyCollection(Of Thing)
Public Sub New()
MyBase.New(GetItems())
End Sub
Private Shared Function GetItems() As IList(Of Thing)
'Generate items as appropriate.
Return {New Thing, New Thing, New Thing}
End Function
End Class
It's rather a simple question and both will work. But I'm just wondering what the best practice is. When a child class changes a variable in the baseclass. Should it call the property or just change the underlying variable.
I'm not using the property to do something with the data. The code in the child class is the same. But what is considered the best practice OOP wise?
Sample code:
Public Class TestDirect
Protected temp As Integer
End Class
Public Class TestChldDirect
Inherits TestDirect
Public Sub New()
MyBase.temp = 1
End Sub
End Class
versus
Public Class TestProperty
Private _temp As Integer
'
Public Property temp() As Integer
Get
Return Me._temp
End Get
Set(ByVal value As Integer)
Me._temp = value
End Set
End Property
End Class
Public Class TestChldProperty
Inherits TestProperty
Public Sub New()
MyBase.temp = 1
End Sub
End Class
The second approach gives you more flexibility later on and better protects/hides your underlying implementation. For instance, in your example you might want to modify the type of temp, add some validation etc. Those changes would be more difficult in your first example as you would be affecting the classes that derive from your base class. In the second example you can make the change without affecting any derived classes.
Basically, the readonly keyword doesn't let me modify a field after I first create the class instance. I could use a property but in this case its just extra overhead. Is there a keyword to make a class field readonly from only outside the class?
make the field private, provide getter and setter for it.
Make the setter private.
This way the value can be seen from outside the class by the getter,but, cannot be set/written from outside the class.
this makes the property read-only from outside the class.
As others have stated, use a property. If you don't want to split the property into one Getter and one Setter then make the setter private.
Public Class Foo
Public Property Abc() As Object
Get
Return Me.m_Abc
End Get
Private Set(value As Object)
Me.m_Abc = value
End Set
End Property
Private m_Abc As Object
End Class
However: The common way is to set the access level of the field to Friend making it accessible within the same assembly, but not from outside the assembly.
Public Class Foo
Public ReadOnly Property Abc() As Object
Get
Return Me.m_Abc
End Get
End Property
Friend m_Abc As Object
End Class
No there isn't. This type is scenario is precisely why properties are provided in the first place. You get a whole lot of flexibility.
However, if you insist you want to use a read only field, you can use reflection to change the value:-
Public Class TestClass
Public ReadOnly MyNumber As Integer
Public Sub New()
'Readonly fields can only be changed this way
'in the constructor
Me.MyNumber = 900
End Sub
Public Sub ChangeNumber(ByVal num As Integer)
SetNumber(num)
End Sub
Private Sub SetNumber(ByVal num As Integer)
Dim fi = Me.GetType.GetField("MyNumber")
'Reflection can change the value of
'a read only field after construction
fi.SetValue(Me, num)
End Sub
End Class
Note that this is a very terrible thing. Reflection shouldn't be used for this sort of thing as you're going to take a performance hit. Just use properties and save yourself the trouble.
While researching Assembly.GetInterfaces(), I found the method was a MustOverride method. Which in my understanding means it has no default action to derived classes. Its just a signature basically, an abstract method. Yet, I can still use it on a type and it will return all implemented interfaces without writing any code for the MustOverride method.
Where is this code that has slipped into the MustOverride method? Have I somehow indirectly overridden it just simply by calling the method on a created type?
This question is purely on the basis of study and discovery, I am not trying to do anything other than understand the confines of the language.
Here is the code I used:
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim t As Type
Debug.WriteLine(GetType(Integer))
t = GetType(Integer)
Dim interfaceArr As Type() = t.GetInterfaces
For i As Integer = 0 To interfaceArr.Length - 1
Debug.WriteLine(interfaceArr(i))
Next
End Sub
End Class
Output Is:
System.IComparable
System.IFormattable
System.IConvertible
System.IComparable 1[System.Int32]
System.IEquatable 1[System.Int32]
Any MustOverride method can always be called on an instance of any type because you couldn't possibly create an instance of a class unless the class provides concrete implementations of all of the MustOverride methods. In this case, your confusion is that you are assuming that the t variable is referencing a Type object, but that is not the case. Since Type is a MustInherit class, it's impossible to ever instantiate an object of that type directly. You could only ever instantiate an object of a class that derives from Type. If you use the debugger to inspect the T variable, you will notice that it is actually referencing an instance of the RuntimeType class, which is an undocumented class which obviously derives from Type.
For instance, consider this example, which duplicates the behavior:
Public Class Form1
Public MustInherit Class BaseClass
Public MustOverride Function GetGreeting() As String
End Class
Public Class DerivedClass
Inherits BaseClass
Public Overrides Function GetGreeting() As String
Return "Hello world"
End Function
End Class
Public Function GetInstance() As BaseClass
Return New DerivedClass()
End Function
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim t As BaseClass = GetInstance()
Debug.WriteLine(t.GetGreeting())
End Sub
End Class
As you can see, the t variable is of the BaseClass type, but it's actually referencing a DerivedClass object. Therefore, even though the BaseClass class defines the method as MustOverride, you can still call it because the actual type of the object does implement it.