What do the Get and Set commands in Visual Basic do? I encountered the Get and Set commands in my book's chapter on Object-Oriented Programming. Here is some code:
Public Property SocSecNum() As String
Get
Return m_ssn
End Get
Set(value As String)
m_ssn = value
End Set
End Property
When you have a field, i.e. a member variable, you can get its value:
var = someObject.SomeField
or you can set its value:
someObject.SomeField = someValue
The whole point of a property is that, from the outside, it behaves just like a field but, from the inside, it behaves like methods. That means that you can get and set a property just like you can a field but, on the inside, rather than just straight assignment and retrieval, it actually executes the Get and Set part of the property definition.
When your project is compiled, they are actually implemented as methods and the property value is stored elsewhere. That might be a dedicated field or it might not. An example of the latter is the Lines property of a TextBox. The is no dedicated field for that data. What actually happens is that the Get part takes the current text, splits it on line breaks and returns the resulting array. Similarly, the Set part combines the array provided into a single String and sets the Text. Two of the most common reasons for extra code are validation and raising events.
A simple field declaration looks like this:
Public SomeField As SomeType
A fully-implemented property looks like this:
Private someField As SomeType
Public Property SomeProperty As SomeType
Get
Return someField
End Get
Set(value As SomeType)
someField = value
End Set
End Property
As you can see, the field is private and, when the property is invoked in code, the getter and setter will get or set the value of that field. Once compiled, that code actually looks like this:
Private someField As SomeType
Public Function get_SomeProperty As SomeType
Return someField
End Function
Public Sub set_SomeProperty(value As SomeType)
someField = value
End Sub
As you can see, the getter and setter are literally methods and the property is just syntactic sugar. Java doesn't have properties and Java developers literally write these two methods as accessors for a field.
More recently, the authors of VB have realised that it's laborious to write all that code out over and over for lots of properties so we now have auto-properties. You can simply write this:
Public Property SomeProperty As SomeType
and everything else is implied. There's still a backing field and there is still a getter and a setter but your code is not cluttered up by showing them. This is how you will write most properties these days. As I said before, you'll still need to write the property out in full if you want to add any extra functionality to the getter or setter. Raising a changed event is probably the most common example of that, e.g.
Public Event SomePropertyChanged As EventHandler
Private someField As SomeType
Public Property SomeProperty As SomeType
Get
Return someField
End Get
Set
If someField <> value Then
someField = value
OnSomePropertyChanged(EventArgs.Empty)
End If
End Set
End Property
Protected Overridable Sub OnSomePropertyChanged(e As EventArgs)
RaiseEvent SomePropertyChanged(Me, e)
End Sub
In this case, when the property is set, the backing field is set if and only if the new value is different to the old value and then an event is raised to notify listeners of that change in property value. The fact that properties can include extra code like this is why they should be used over fields for public data.
On the Tools menu select Options. Click on Debugging. Uncheck Step over properties and operators
Create your class thusly. It can be nested in the form class for this little test.
Public Class SomeClass
Private m_ssn As String
Public Property SocSecNum() As String
Get
Return m_ssn
End Get
Set(value As String)
m_ssn = value
End Set
End Property
End Class
Now, in the Button.Click create an instance of your class. Set the SocSecNum. Then Get the value and assign it to a text box.
Next set a break point in your code on the first line of the button code. Do this by positioning your cursor on the gray line to the left of the code and clicking.
Run your program and click the button. You will encounter the break point. You can now step through your code line by line. Use the Step into symbol.
You will see that when you set the property the Property Procedures Set is called. The value is stored in the Private m_ssn variable. Likewise, when you get the property the value of the private variable is returned. Hold your cursor over the m_ssn variable when you are in the Property Procedure and you will see the value stored there.
All this boilerplate code disappears in current versions of Visual Studio where the default Getter, Setter and private member are created for you behind the scenes with auto properties. You can still write the Get and Set and private variable when you need to do things to the input or output.
Here's a little that can give you an idea
I used this if to easily disable some buttons while process is running
and then when you call the "decrypting = false" the disabled buttons will become enabled
Dim dec As Boolean
Private Property decrypting As Boolean
Get
Return dec
End Get
Set(value As Boolean)
dec = value
partialFile = False
btnAddFiles.Enabled = Not dec
btnAddFolder.Enabled = Not dec
btnAddKeyFile.Enabled = Not dec
btnSaveAs.Enabled = Not dec
txtOutputFileName.Enabled = Not dec
txtOutputFolder.Enabled = Not dec
txtInputFilePath.Enabled = Not dec
txtKeyFilePath.Enabled = Not dec
LV.Enabled = Not dec
btnDecrypt.Enabled = Not dec
btnAbort.Enabled = dec
If Not decrypting Then
Me.Text = "TS Decrypter and Joiner v1.0"
End If
End Set
End Property
Related
Although an experienced VBA programmer it is the first time that I make my own classes (objects). I am surprised to see that all properties are 'duplicated' in the Locals Window. A small example (break at 'End Sub'):
' Class module:
Private pName As String
Public Property Let Name(inValue As String)
pName = inValue
End Property
Public Property Get Name() As String
Name = pName
End Property
' Normal module:
Sub Test()
Dim objTest As cTest
Set objTest = New cTest
objTest.Name = "John Doe"
End Sub
Why are both Name and pName shown in the Locals Window? Can I in some way get rid of pName?
As comments & answers already said, that's just the VBE being helpful.
However if you find it noisy to have the private fields and public members listed in the locals toolwindow, there's a way to nicely clean it up - here I put the Test procedure inside ThisWorkbook, and left the class named Class1:
So what's going on here? What's this?
Here's Class1:
Option Explicit
Private Type TClass1
Name As String
'...other members...
End Type
Private this As TClass1
Public Property Get Name() As String
Name = this.Name
End Property
Public Property Let Name(ByVal value As String)
this.Name = value
End Property
The class only has 1 private field, a user-defined type value named this, which holds all the encapsulated data members.
As a result, the properties' underlying fields are effectively hidden, or rather, they're all regrouped under this, so you won't see the underlying field values unless you want to see them:
And as an additional benefit, you don't need any pseudo-Hungarian prefixes anymore, the properties' implementations are crystal-clear, and best of all the properties have the exact same identifier name as their backing field.
All the Inspection windows not only show the public interface of the objects to you, but also their private members. AFAIK there is nothing you can do about it.
Consider it a nice feature to get even more insights while debugging.
In my experience this is less of an issue in real world objects as they tend to have more fields and properties. Assuming a consistent naming (as your example shows), fields and properties are nicely grouped together.
If you really dont want to see even Mathieu's This you could wrap it into a function. This is a bit more involved, and can be achieved using
a second class that stores the data in public variables. This will be marginally slower then Mattieu's implementation
a collection object that accesses the data using keys. This does not require additional clutter in the project exporer's 'class module' list but will be a little slower if you call the This repeatedly in fast sucession
An example for each is given below. If you break in the Class's Initialisation function, you can add me to the watch window and only the Name property will be listed
Using 2 Objects example
insert a classmodule and name it: InvisibleObjData
Option Explicit
Public Name As String
Public plop
Private Sub Class_Initialize()
Name = "new"
plop = 0
End Sub
insert a classmodule and name it: InvisibleObj
Option Explicit
Private Function This() As InvisibleObjData
Static p As New InvisibleObjData 'static ensures the data object persists at successive calls
Set This = p
End Function
Private Sub Class_Initialize()
This.Name = "invisible man": Debug.Print Name
Me.Name = "test": Debug.Print Name
This.plop = 111: Debug.Print This.plop
End Sub
Property Let Name(aname As String): This.Name = aname: End Property
Property Get Name() As String: Name = This.Name: End Property
'_______________________________________________________________________________________
' in the immediate window type
'
' set x=new invisibleObj
If you dont like splitting the class over two objects, a similar behaviour can be generated using a 'wrapped' collection object:
insert a classmodule and name it: InvisibleCol
Option Explicit
Private Function This() As Collection
Static p As New Collection
'static ensures the collection object persists at successive calls
'different instances will have different collections
'note a better dictionary object may help
Set This = p
End Function
Private Function add2this(s, v)
'a better dictionary object instead of the collection would help...
On Error Resume Next
This.Remove s
This.Add v, s
End Function
Private Sub Class_Initialize()
add2this "name", "invisible man": Debug.Print Name
Me.Name = "test": Debug.Print Name
add2this "plop", 111
Debug.Print This("plop") ' use the key to access your data
Debug.Print This!plop * 2 ' use of the BANG operator to reduce the number of dbl quotes
' Note: This!plop is the same as This("plop")
End Sub
Property Let Name(aname As String): add2this "name", aname: End Property
Property Get Name() As String: Name = This!Name: End Property
'_______________________________________________________________________________________
' in the immediate window type
'
' set x=new invisibleCol
I have a simple question.
Is there a difference between a one-line ReadOnly Property and a ReadOnly Property with an explicit Get Statement?
For example, In Visual Studio 2010 I used to have to do this:
Private _Message As String = ""
Public ReadOnly Property Message As String
Get
Return _Message
End Get
End Property
Public Sub New(Message As String)
_Message = Message
End Sub
But now here I am in Visual Studio 2015 and it let's me get away with this:
Public ReadOnly Property Message As String
Public Sub New(Message As String)
Me.Message = Message
End Sub
Now I wasn't trying to me lazy, I just ran across it, expecting IntelliSense to force the Get Statement. So I'm left wondering if there is a difference that I should be aware of. Is it bad practice or does it do something different?
I've managed to find tons of topics and forums about ReadOnly in C#, but I seem to be coming up dry when searching the topic regarding vb.net.
I'll appreciate any input.
Thanks! =)
Properties without explicit getters and setters are simply a syntactic convenience. There's no difference in implementation. You should generally use the second option because it makes your code more readable. Only use the first option if you need extra functionality in the getter.
The accepted answer appears to be untrue, at least for me in Visual Studio 2022.
When a property is not a constant, you have to use the multiline declaration otherwise the initial value is cached
This
Private ReadOnly Property ConnectionFile As String
Get
Return gsapppath & "\connection.txt"
End Get
End Property
returns the correct path whereas this
Private ReadOnly Property ConnectionFile As String = gsapppath & "\connection.txt"
did not include the path because the variable gsapppath was not initialized at the time the class was created, yet it was initialized at the time the property was fetched.
So it's not just a syntactic convenience, there is a functional difference.
VB.net gets mad when I say:
Public thisTicket as new ticket
and insists I need to use Dim instead of Public, but this prevents me from accessing the object outside of the sub I declared it in.
Basically I am trying to do the following in one sub:
Public thisTicket as new ticket
thisTicket.completed = true
and then this in another sub:
if thisTicket.completed = true then
'do this
else
'do this instead
end if
Class:
Public Class ticket
Property hasStatusChange As Boolean
Property initialAssignmentStatus As String
Property initialApprovalStatus As String
Property initialCompletionStatus As String
Property newApprovalStatus As String
Property newCompletionStatus As String
Property newAssignmentStatus As String
Property wasUpdated As Boolean
End Class
Another question,
If I made a new property under ticket called "completionChanged as boolean"
and the criteria for that being true or false would be if the initialCompletionStatus was not equal to the newCompletion status (then it would be true),
how would I get it so that I could say:
if thisTicket.completionchanged = true then
and have it know on it's own whether it's true or not, without me having to spell out the if/then each time?
A Local variable in a Sub or Function or property getter or setter (i.e. in a method) lives only while the method is being executed. Before or after the call of this method, the local variable does not exist. Therefore modifiers like Public make no sense there.
If you need to access a variable from outside a method, make it a class member (either a field or a property).
Answer to second question. This is why properties exist. You can add logic to them that is executed when they are accessed.
Private m_completionStatus As String
Property CompletionStatus() As String
Get
Return m_completionStatus
End Get
Set(ByVal Value As String)
If Value <> m_completionStatus Then
m_completionStatus = Value
completionChanged = True
End If
End Set
End Property
And then please write
If thisTicket.completionchanged Then
and drop the " = true ". What the If-statement wants is an expression yielding a Boolean result, and a Boolean variable is such an expression in itself. There is not need to have any comparison in an If-statement.
If completionchanged is True then completionchanged = True is True also. If completionchanged is False then completionchanged = True is False also. Comparing with "= True" is a bit like multiplying a number with 1. It doesn't change anything.
If you would like it to be a member of the class, such as a property.
Public Property ThisTicket As Ticket
And in your class' constructor initialize it.
Public Sub New()
ThisTicket = new Ticket
End Sub
Now you will be able to access this ticket from inside of your class. However, consider whether other places in your code really need to know about this member. If they do not, make it private. If you may extend this class in the future where the subtypes would need access, make it protected.
If you decide to make it private, you can bypass making it a property and just create it as a field.
Private _thisTicket As Ticket
You should also ask yourself if thisTicket really belongs to the state of the containing object. Does the containing object process a lot of tickets? Maybe the ticket should be passed into the processing function instead of being contained. If the containing object's state does concern itself with only that one ticket throughout its life, then making it a property is valid.
As for your second question..
Create a function in Ticket to evaluate the business logic you mentioned and return the boolean value for if it has changed or not.
Public Function HasStatusChanged As Boolean
logic...
End Function
I'm trying to create a new property with a type of StringDictionary but when I use the property like a stringdictionary variable I am getting an error.
The goal is to NOT use a variable, I need to use a property for this. Behind the scenes I am saving the stringdictionary value to a global user-ID indexed collection. Here's the code that creates the property and attempts to get and set:
Public Property MaxLenDict As StringDictionary()
Get
Return GetFromCollection("MaxLenDict")
End Get
Set(Value As StringDictionary())
SaveToCollection("MaxLenDict", Value)
End Set
End Property
Public Sub ExampleSub()
If MaxLenDict("hello world") = "" Then MaxLenDict.Add("Hello World", "I'm Here")
End Sub
Get this error in ExampleSub "StringDictionary cannot be converted to string" in the IF statement on this code:
MaxLenDict("hello world")=""
So how do I successfully make and use a property as a stringdictionary?
Your property is of type StringDictionary() (an array!), not StringDictionary.
I’m not sure that using StringDictionary is advised in the first place, though. The class is simply a remnant from pre-generics versions of .NET. Use Dictionary(Of String, String) instead.
I am trying to figure out how to set the value of the text that is displayed for each item of a list control, such as a checkbox list, but really this applies to most list controls, not just the checklistbox control.
I have a checklistbox control,
Friend WithEvents clstTasks As System.Windows.Forms.CheckedListBox
that I usually want to populate with a list of task names. I call the add method to add a Task object to the list. I know that if I override the ToString method, whatever value is returned by that function will be displayed as the text for the list item.
However, in rare situations, I want to display something else other than just the name. For example, perhaps I want to display the name and the value of another property, such as the value of the Boolean property "Optional" shown in parenthesis following the name.
What is the best way to do this?
The best that I can think of is to define a property which is set in the GUI layer and then used by the ToString function to determine how it should behave when called. If the controlling property is set to one value, the ToString will return the Name, else it will return the Name followed by the value of the Optional flag. This seems a little disjoint a kludged to me.
The other alternative which seems a little overkill is to define a new class which inherits from Task, for example TaskOptional, which overrides the Tostring method on the base class. In this subclass, the ToString function would return the Name/Optional flag value. However, this, too, seems a little nuts to have to come up with a new class just to modify how the text is being displayed in the presentation layer. I feel that I should be able to control this in the presentation layer without changing the business object or creating a new derived object.
What is the best way to accomplish this?
For Each CurrentTask As Task In _MasterTaskList
clstTasks.Items.Add(CurrentTask, True)
Next
Public Class Task
Private _Name As String
Private _Optional As Boolean
Public Sub New (name As String, optional As Boolean)
_Name = name
End Sub
Public Overrides Function ToString() As String
Return _Name
End If
End Function
End Class
You can set the DisplayMember property of your CheckedListBox to the name of one of your custom class' property.
Let's say you create a property like the following:
Public ReadOnly Property NameOptional() As String
Return _Name & " (" & _Optional & ")"
End Property
then you can set the display member like this:
clstTasks.DisplayMember = "NameOptional"
When you set a display member, this property will be displayed instead of the ToString value.
You could do the following
Public Overrides Function ToString() As String
Return string.Format("{0}{1}", _Name, IIF(_Optional, " (Optional)", ""))
End Function
EDIT: You will have to set the value of _optional in the constructor, which is missing in the code you have provided.