VB.NET setting class property initial value - vb.net

When creating a object based on a class. There are certain properties that I prefer to not be value 0 or nothing. So I would like to set the initial value to 1.
Is this best done via the constructor?
Class Product
Public Property Price As Decimal
Public Sub New()
Price = 1
End Sub
End Class
Or can you also write it as following? Does this second version make the value fixed at 1 or can you also alter the value if It's written like this?
Class Product
Public Property Price As Decimal = 1
End Class

Either way you do it, it'll function the same, however do defer to how your team normally does it to maintain consistency.
However, if you do have instances where you may open up the constructor to allow setting of those properties on initialization based on some argument given to the constructor, I would opt to always setting it in the constructor for consistency. If the property always has a default value of X on initialization then inline it at the top so it stands out.
Basic Example:
Class Product
Public Property Price As Decimal = 1
Public Property Quantity As Integer
Public Sub New()
Quantity = 0
End Sub
Public Sub New(quantity As Integer)
Quantity = quantity
End Sub
End Class

At the end its the same, in both cases you can change the values.

No difference, you can alter the value unless it's const or readonly. If you do the second, the compiler will sort of convert it like your first version. Here's a little program that'll show you. This will display 0 and then 1.
Module Module1
Sub Main()
Dim o As New B
Console.ReadLine()
End Sub
End Module
MustInherit Class A
Public Sub New()
Show()
End Sub
Public MustOverride Sub Show()
End Class
Class B
Inherits A
Private test As Integer = 1
Public Sub New()
MyBase.New()
' Value for test is being set here
Show()
End Sub
Public Overrides Sub Show()
Console.WriteLine(test)
End Sub
End Class

Related

Advantage or Disadvantage between Two Class Modules

What would be the advantages or disadvantages of using Class1 instead of Class2?
The quantity information stored in each instance of the class will be adjusted up and down as needed (via the functions, and while it seems to make sense to me that I would only need to make these variables public so that they are visible from outside the class, I feel that there is most likely some reason that this shouldn't been done.
Class1
Option Explicit
Public Sequence As String
Public Quantity As Double
Public Sub AddQty(sAddQty As Double)
Quantity = Quantity + AddQty
End Sub
Public Sub SubQty(sSubQty As Double)
Quantity = Quantity - sSubQty
End Sub
Class2
Option Explicit
Private iSeq As String
Private iQty As Double
Public Property Get Qty() As Double
Qty = iQty
End Property
Public Property Let Qty(lQty As Double)
iQty = lQty
End Property
Public Property Get Sequence() As String
Sequence = iSeq
End Property
Public Property Let Sequence(lSeq As String)
iSeq = lSeq
End Property
Public Sub AddQty(sAddQty As Double)
iQty = iQty + AddQty
End Sub
Public Sub SubQty(sSubQty As Double)
iQty = iQty - sSubQty
End Sub
In terms of interfaces, the two are exactly equivalent, because public fields are exposed as Property members. If you added a 3rd class module and wrote this:
Implements Class1
You would be forced by the compiler to add these members:
Private Property Get Class1_Sequence() As String
End Property
Private Property Let Class1_Sequence(ByVal RHS As String)
End Property
Private Property Get Class1_Quantity() As Double
End Property
Private Property Let Class1_Quantity(ByVal RHS As Double)
End Property
Private Sub Class1_AddQty(sAddQty As Double)
End Sub
Private Sub Class1_SubQty(sSubQty As Double)
End Sub
If you added another class module and wrote this:
Implements Class2
You would be forced by the compiler to have essentially the exact same members:
Private Property Get Class2_Sequence() As String
End Property
Private Property Let Class2_Sequence(ByVal RHS As String)
End Property
Private Property Get Class2_Qty() As Double
End Property
Private Property Let Class2_Qty(ByVal RHS As Double)
End Property
Private Sub Class2_AddQty(sAddQty As Double)
End Sub
Private Sub Class2_SubQty(sSubQty As Double)
End Sub
When properties do nothing and there's no incentive to properly encapsulate their values, go ahead and have public fields.
However there's little need for AddQty or SubQty instance methods when the backing field exposes a Property Let accessor - one could simply do foo.Quantity = foo.Quantity + 2 instead. An API that appears to provide multiple ways to do the same thing, is a confusing API.
So what you do, is you define an explicit interface that defines the API you want to work with:
Public Property Get Quantity() As Double
End Property
Public Property Get Sequence() As String
End Property
Public Sub AddQty(ByVal value As Double)
End Sub
Public Sub SubQty(ByVal value As Double)
End Sub
And then make your class Implements this interface (say, ISomething), and the rest of the code works with this ISomething interface that only exposes the members you want it to be able to work with - and that excludes the class' Property Let members; the rest of the code only sees what it needs to see, and can only access what it needs to access.
Dim foo As ISomething
Set foo = New Something
'foo.Quantity = 42 ' illegal
Dim bar As Something
Set bar = foo
bar.Quantity = 42 ' ok
bar.AddQty 2
Debug.Print foo.Quantity ' should be 44

Access "sender" without passing argument

I have a Main class in which I have a populated array (population now shown in this example) of the Tomato object. I want to access a specific Tomato and use the LowerPrice subroutine without passing a sender object.
Public Class Main
Dim TomatoArray() As Tomato
Private Sub HaveATomatoSale
'This is how I want to do my price reduction
TomatoArray(0).LowerPrice(10)
'This is NOT how I want to do my price reduction, i.e. including a sender object
TomatoArray(0).LowerPrice(TomatoArray(0), 10)
End Sub
End Class
I also have a function inside the Tomato class like this:
Public Class tomato
Dim tomato_price As Integer = 15
Public Property Price As Integer
Get
Return tomato_price
End Get
Set(value As Integer)
tomato_price = value
End Set
End Property
Public Sub LowerPrice(ByVal Decrease As Integer)
'What should I point to here?
sender.tomato_price -= Decrease
End Sub
End Class
I have searched both SO, MSDN and the rest of the internet for a simple answer to this seemingly simply questions but to no avail (probably due to my lack of a good keyword for this!). How can I do this? Thanks!
The keyword you are looking for is Me:
Public Sub LowerPrice(ByVal Decrease As Integer)
Me.tomato_price -= Decrease
End Sub
Note that Me is optional, so the following would work as well:
Public Sub LowerPrice(ByVal Decrease As Integer)
tomato_price -= Decrease
End Sub
In fact, you might want to take advantage of automatic properties and reduce your code to:
Public Class tomato
Public Property Price As Integer = 15
Public Sub LowerPrice(ByVal decreaseBy As Integer)
Me.Price -= decreaseBy
End Sub
End Class

Call baseclass property

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.

Hiding function on nested class

Public Class Class1
Private names As List(Of String)
Private _class2 As New Class2
Public Sub AddName(ByVal name As String)
names.Add(name)
_class2.Add()
End Sub
Public ReadOnly Property AddAge(ByVal name As String) As Class2
Get
_class2.index = names.IndexOf(name)
Return _class2
End Get
End Property
Public Sub Clear()
names.Clear()
_class2.Clear()
End Sub
Public Class Class2
Private _age As List(Of Integer)
Protected Friend index As Integer
Public Property Age() As Integer
Get
Return _age(index)
End Get
Set(ByVal value As Integer)
_age(index) = value
End Set
End Property
Public Sub Add()
_age.Add(0)
End Sub
Public Sub Clear()
_age.Clear()
End Sub
End Class
End Class
How can I hide ,Sub Clear and Sub Add on class2, so they'll only be visible on class1, like;
Public Sub Clear()
names.Clear()
_class2.Clear() '<<<<<<<
End Sub
I want they do not be visible on Sub Main(), like they are below.
Sub Main()
Dim person As New Class1
person.AddAge("kid").Clear() '<<<<<<
person.AddAge("kid").Add() '<<<<<<
End Sub
If I put Protected, I class1 cannot access it. If I put Protected Friend, Sub Main() can still access them. Thanks for your help and time.
Used -Hans Passant- comment.
"Trust in .NET follows assembly boundaries. If you get two classes in one assembly then there are two programmers that know how to find each other if there's a problem. The only way to get what you want is to put these classes in a separate class library project. Which then lets you use Friend. And whomever writes that Main method doesn't have to be friendly."

.net dynamic loading

I've seen some other responses about this and they talk about interfaces but I'm pretty sure you can do this with classes and base classes but I can't this to work.
Public Class Behavior
Private _name As String
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public Property EditorUpdate As Boolean
Public Sub New(ByVal name As String)
_name = name
EditorUpdate = False
End Sub
Public Overridable Sub Update()
End Sub
' runs right away in editor mode. also runs when in stand alone game mode right away
Public Overridable Sub Start()
End Sub
' runs after game mode is done and right before back in editor mode
Public Overridable Sub Finish()
End Sub
' runs right when put into game mode
Public Overridable Sub Initialize()
End Sub
' runs when the game is complete in stand alone mode to clean up
Public Overridable Sub Destroy()
End Sub
End Class
Public Class CharacterController
Inherits Behavior.Behavior
Public Sub New()
MyBase.New("Character Controller")
End Sub
Public Overrides Sub Update()
' TODO: call UpdateController()
' THINK: how can UpdateController() get the controller entity it's attached to?
' Behaviors need a way to get the entity they are attached to. Have that set when it's assigned in the ctor?
End Sub
End Class
Dim plugins() As String
Dim asm As Assembly
plugins = Directory.GetFileSystemEntries(Path.Combine(Application.StartupPath, "Plugins"), "*.dll")
For i As Integer = 0 To plugins.Length - 1
asm = Assembly.LoadFrom(plugins(i))
For Each t As Type In asm.GetTypes
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
behaviorTypes.Add(t.Name, t)
Dim b As Behavior.Behavior
b = CType(Activator.CreateInstance(t), Behavior.Behavior)
'Dim o As Object = Activator.CreateInstance(t)
End If
End If
Next
Next
When it tries to convert whatever Activator.CreateInstance(t) returns to the base class of type Behavior I'm getting invalid cast exception. That type should be of CharacterController which is defined as a child of Behavior so why wouldn't it let me cast that? I've done something like this before but I can't find my code. What am I missing?
This may not be an answer to your question (it also might resolve your exception -- who knows), but it is something that needs to be pointed out. These lines:
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
Should really be changed to one conditional like this one:
If t.IsPublic AndAlso (Not t.IsAbstract) AndAlso _
GetType(Behavior.Behavior).IsAssignableFrom(t) Then
Otherwise, if somebody defines a random type called "Behavior" in their own assembly and derives it from another type, your code will think it is a plugin. Additionally, if someone derives your Behavior type and then derives that type (two levels of inheritance) this code will incorrectly skip over that type. Using the IsAssignableFrom method is a quick and easy way to ensure that one type does actually derive from the specific type you want (instead of any type that shares the same name), even if there is another type in between your types in the inheritance tree. The additional check against t.IsAbstract will also ensure that you don't try to instantiate an abstract subtype of your base plugin type.
This works for me:
Dim ctor As Reflection.ConstructorInfo = _
t.GetConstructor(New System.Type() {})
Dim o As Object = ctor.Invoke(New Object() {})
Dim plugin As Plugin = TryCast(o, Plugin)
(If I find t, I invoke the parameterless constructor.)
[I just realized this is probably what Activator.CreateInstance does, so I replaced my code with yours and it worked your way -- so this probably won't help you]