Access "sender" without passing argument - vb.net

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

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

VB.NET setting class property initial value

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

I cant inherit form my parent class

I'm having a problem that I cant figure out, I'm trying to code a payroll system with vb.net 2013, I must use a console application.
I'm using classes to calculate the salary depending on the amount of weeks that the employee's worked, but the problem is that I cannot inherit from my parent class, I keep on getting this error and i don't know how to fix it.
Error 2 Class 'Employee_Payment_System.Employee' has no accessible 'Sub New' and cannot be inherited.
Whenever i try to add a 'sub new' I get this
Error 3 'Public Sub New()' has multiple definitions with identical signatures.
Here is my code so far
Public Class Employee
#Region "Private Declarations"
Private EMP_FullName As String
Private EMP_LastName As String
Private EMP_Salary As Double
Private EMP_Number As Integer
Private EMP_Address As String
#End Region
'The values are obtained from the public properties coded below
Private Sub New()
EMP_FullName = ""
EMP_LastName = ""
EMP_Salary = 0.0
EMP_Address = ""
End Sub
Private Sub New(ByVal FullName As String, ByVal LastName As String, ByVal Address As String, ByVal Salary As Double, ByVal Number As Integer)
EMP_FullName = FullName
EMP_LastName = LastName
EMP_Address = Address
EMP_Number = Number
EMP_Salary = Salary
End Sub
' the properties gets their values from the console
Public Property FullName() As String
Get
Return EMP_FullName
End Get
Set(value As String)
EMP_FullName = value
End Set
End Property
Public Property LastName() As String
Get
Return EMP_LastName
End Get
Set(value As String)
EMP_LastName = value
End Set
End Property
Public Property Address() As String
Get
Return EMP_Address
End Get
Set(value As String)
EMP_Address = value
End Set
End Property
Public Property Salary() As Double
Get
Return EMP_Salary
End Get
Set(value As Double)
EMP_Salary = value
End Set
End Property
Public Property Number() As Integer
Get
Return EMP_Number
End Get
Set(value As Integer)
EMP_Number = value
End Set
End Property
Public Overridable Function Payment() As Double
Return EMP_Salary
End Function
End Class
Public Class WeeklySalary : Inherits Employee
'The main problem "Error 2 Class 'Employee_Payment_System.Employee' has no accessible 'Sub New' and cannot be inherited." generates here
Sub New()' I get my second error "Error 3 'Public Sub New()' has multiple definitions with identical signatures. " here
End Sub
Private NumberOfWeeks As Integer
Private Sub New() 'The second Error is related to this sub
NumberOfWeeks = 0
End Sub
Public Sub New()
NumberOfWeeks = Number
End Sub
Public Property Number_Of_Weeks() As Integer 'This is the number of weeks worked
Get
Return NumberOfWeeks
End Get
Set(value As Integer)
NumberOfWeeks = value
End Set
End Property
Public Overrides Function Payment() As Double
Return NumberOfWeeks * Salary 'Calculates Weekly salary here
End Function
End Class
I still have to code one more Class for monthly payments, and I still need to code the module for the console interface but I need my classes to work before I can start the module (For testing purposes).
Okay I have managed to fix it, I have changed my private constructors to public and it worked thank you
As jonrsharpe says: Why are you making your constructors (Sub New) private? When adding a Public Sub New without parameters it clashes with Private Sub New(). You cannot have to methods with the same name and foot print.
But what are you trying to achieve by inheriting from Employee? If you are trying to do some EmployeeWorkRecord class it should probably aggregate Employee and work records and not inherit from it.
I'm not writing this as a criticism, just as a general pointer about inheritance.
The idea of inheritance is that child classes are the same kind of object as the parent class, but with extra functionality.
To have a real world comparison, Somebody's wages aren't the same as the somebody.
However you could have for example a class Human. A child class of that could be an employee while another child class could be a customer. Each with their own properties.
An employee could have a StartOfEmployment property while a customer could have for example a LoyaltyCardPoints property, and they could both have ContactAddress and Gender.
Anyway. To the answer - or one possible one at any rate
I suspect that you're looking at inheritance the wrong way round. The employee could possibly inherit the WeeklySalary class, while a class of Manager could possibly inherit from a MonthlySalary Class. But you'd probably be better setting up some sort of datatable using employee numbers as a key. if you do it the way youre doing at the moment, you'd have a lot of either duplicated or unused data in your program.
The naming of parent and child classes sometimes is counter intuitive as we often think as children as being smaller than the parents, but in programming, the child is the same as the parent but with extra funtionality.

Trying to fire events from a Class that is within a Dictionary instantiated on the MainForm

Trying to have events raised within a class be received within the MainForm when this class is within a Dictionary. Here are some code samples.
Created a Class:
Public Class Zone
Public _ZoneID As Integer
Public _ZoneName As String
Public Event ZoneEntered(ByVal intToolID As Integer, ByVal intZoneID As Integer)
Public Sub New()
End Sub
Public Property ZoneName() As String
Get
Return _ZoneName
End Get
Set(value As String)
_ZoneName = value
End Set
End Property
Public Property ZoneID() As Integer
Get
Return _ZoneID
End Get
Set(value As Integer)
_ZoneID = value
End Set
End Property
Public Sub CheckZone(ByVal intToolID As Integer)
If intToolID > 0 Then
RaiseEvent ZoneEntered(intToolID, _ZoneID)
End If
End Sub
End Class
Within the FormMain code we have:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim Zones As New Dictionary(Of Integer, Zone) 'Holds all the Zones for all CameraGroups within this Workspace
Dim NewZone As Zone
NewZone.ZoneName = "TestZone"
NewZone.ZoneID = 123
Zones.Add(1, NewZone)
Dim intZoneID As Integer = 1
If Zones.ContainsKey(intZoneID) Then
Dim ZoneActive As Zone
ZoneActive = Zones(intZoneID)
If Not (ZoneActive Is Nothing) Then
ZoneActive.CheckZone(intZoneID) 'This would fire the event I am looking for
End If
End If
End Sub
How do I setup events from with the class that is part of a dictionary?
There are several things wrong before I can get to an answer. It is not a good idea to make up you own Event signature. You should use EventName(Sender As Object, e As ZoneEventArgs). If you discover that something else is needed in the event you just need to add it to the EventArgs class rather than refactor gobs of code. For that:
Public Class ZoneEventArgs
Inherits EventArgs
Public Property ToolID As Integer
Public Property ZoneID As Integer
Public Sub New(tool As Integer, zone As Integer)
ToolID = tool
ZoneID = zone
End Sub
End Class
' event sig:
Public Event ZoneEntered(sender As Object, e As ZoneEventArgs)
' raise it:
RaiseEvent ZoneEntered(Me, New ZoneEventArgs(thisTool, thisZone))
Now if/when you run CA, it wont scold you...at least not for that.
Declaring the Dictionary in FormLoad is bad because it will only exist there, but I'll assume that is an artifact of being a demo. To keep it like that, each item added to the collection needs to be hooked up to an event handler. For that, you need there to be only one way in and one way out of the Dictionary:
Friend Sub AddZone(name As String, zID as Integer, key As Integer)
Dim z As New Zone With {.ZoneName = name, .ZoneID = zID)
AddHandler z.ZoneEntered, AddressOf NewZoneEntered
Zones.Add(key, z)
End Sub
Private Sub NewZoneEntered(sender As Object, e As ZoneEventArgs)
' respond
End Sub
You should also have a RemoveZone or DropZone method where the zones are removed from the collection and RemoveHandler used to unhook the handler.
A much better way is to write a collection class. This would handle creating Zone items, catch the events locally and perform the role of the DictionaryKey to you can find them. Then when it catches one of those events, it Raises a similar one for the form or other listeners.
Its a much more flexible approach and gets all the Zone related code out of the form. With the Dictionary the way it is there is, there is nothing to stop other code from adding/removing items - an OO approach using a collection class would.

Global Values in VB

The scenario
I'm creating a simple cafe order form in visual studio.
I have one form to take orders and another as a summery page.
On the orders form i have a button "Save/ New order" when this button is pressed the order gets added to a listbox. I also want to create and accumulator to count the number of orders taken. I decided to create a global variable because i will only use this number in the summary form.
This is what ive done so far.
Public Class GlobalValues
Private Shared pItemIterator As Integer
Public Shared Property orderCounter As Integer
Get
Return pItemIterator
End Get
Set(value As Integer)
pItemIterator = value
End Set
End Property
End Class
And for the button
GlobalValues.orderCounter+= 1
The Problem
I currently stuck on how to minus -1 from this value. On the order form I have a button to remove the last order.
You cannot access pItemIterator as you made it private. You need to use the public Property you made for it:
GlobalValues.orderCounter -= 1
Is it okay for you to make a public sub for it that will deal with pItemIterator?
Public Class GlobalValues
Private Shared pItemIterator As Integer
Public Shared Property orderCounter As Integer
Get
orderCounter = pItemIterator
End Get
Set(value As Integer)
pItemIterator = value
End Set
End Property
Public Shared Sub subtractOneFromOrderCounter()
pItemIterator = pItemIterator - 1
End Sub
End Class
Then you would set subtractOneFromOrderCounter() to be called when your button is clicked.