Hi,
......continued: (continue reading the first part and read on here whenever you're ready)
Class Address
Dim Name As String
Dim Street As String
Sub Show()
MsgBox("Address: " & Name & ", " & Street)
End Sub
End Class
Dim MyFirstAddress As Address
MyFirstAddress = New Address
MyFirstAddress.Name = "name of my first object"
MyFirstAddress.Show()
That's new, isn't it? With your UDTs, you explicitly had to pass the UDT to a procedure
as an argument. Now you just write it differently. This syntax makes clear that the
member (Name, Street or Show) is "part of the class or object".
Ok, you now have seen the difference between a UDT and a Class: A Class can contain
methods. ATM you may doubt that classes are such an important item because you don't
know the other advantages of them yet, but I will tell you about them later because
this isn't the right time yet.
We've also learned what an object and a reference is, and how to access an object's
members.
Being at it, the first thing for today is another explanation of a term: "OOP". OOP is
"object oriented programming". The basic key to it is what you've already learned:
Create objects and work with them. Whenever you're doing OOP it just means
that you are writing and using classes. (I know, one could complicate this description
but I don't.)
Next step in this explanation is dropping the term "UDT". I kept it intentionally
even though it's a VB6 term. Now I reveal the reason for this: As I said before,
in VB.Net, these UDTs/Types are called "Structures". At first sight, they are the same
as the Types in VB6:
Structure Address
Dim Name As String
Dim Street As String
End Structure
You see? No magic here. But now the confusing part: In VB.Net, you can also put
methods into a Structure. Huh?? Wasn't the reason for creating classes the fact
that UDTs can not contain methods? Yes, that was true vor VB6. It is not true
anymore for VB.Net. You are right: there must be another reason why both, classes
and structures, exist. And it is there. Unfortunatelly, I must state the 2nd
time today: Now is not the right time to explain it. In advance, I only say that
the memory management is different between a Structure and a Class. In most
cases, a Class is the right choice, in others a Structure. Later about the
background and how to decide which one to use.
So, from now on I use the word "Structure" instead of "UDT". I hope it's understandable
that I had to keep the term "UDT" in order to give a comprehensible reason why classes
have been introduced. It was (and still is) just not the right time to write about
the real reason in detail. Just remember that it is because of different memory
management. I consider the topic "UDT" closed.
Don't worry, I don't lose sight of our final target:
"Put the file access into a Shared method in a Class.
For this purpose, you first have to know what a Class
and a Shared method is."
We are already at 66% of our way to the destination because we know what
a Class and a method is. What remains is the word "Shared". Let's go on
with it!
Well, shall I give an abstract definition of "Shared" or do you really want
to understand it? With the latter, I must go into details again. But I think
you are ready for them because you want to understand. Here are the details:
As mentioned, an object resides in memory. The more objects you have, the
more memory is used. If one object uses eight bytes, five objects occupy 40 bytes.
If you declare five variables and assign a new object to each variable by
using the New keyword, each variable contains a different memory address.
In other words, the variables contain different references. As writen before,
the field of an object is a member of the object. The fields in our example are
so-called "instance members". That's why I explained the word "instance" before:
As an instance of a Class is also called an object, and as a field is called
a member of a class, a field in our class is also called an instance member.
Read the sentence again, but it's just word twiddling.
In preperation to the following step, I have to add another field to
our class. It's called "ID", an Integer value. Each address object is
to have a unique ID so that we can identify each object by it's ID:
Class Address
Dim Name As String
Dim Street As String
Dim ID As Integer '<---------- a new instance member
Sub Show()
MsgBox("Address: " & ID & ", " & Name & ", " & Street) '<---- ID also added here
End Sub
End Class
Each instance of the Class (=each object) now occupies 4 additional bytes
of memory for the Integer value. This is the place to store the ID in.
Here comes the first version of how to use it:
Dim Address1 As Address
Dim Address2 As Address
Address1 = New Address
Address1.ID = 1
Address1.Name = "Strand"
Address2 = New Address
Address2.ID = 2
Address2.Name = "Zingler"
Here we have two variables and assign a new object to each of them.
The first object carries the ID 1, the second ID 2.
Ok, the ID is another "instance member". As you understand the above, you may ask:
Isn't there a reason why I accentuate the word "instance" in "instance member" or
"instance field" so much? Aren't _all_ fields instance fields? No, they are not!
In our example they are, but not in general: In addition to instance fields there
are "shared fields". So, now we are at it. What is a shared field?
Answer: A shared field is a variable that exists right from the
start of the application til it ends. The field is not part of an object.
We can say that a shared field is part of the type (=of the Class).
Memory for all shared fields is automatically reserved. We don't have
to worry about it and and we don't have to use the New keyword.
It gets clearer if you look at the memory usage. I give an example for
clarification:
Class Address
Dim Shared LastID As Integer '<----------- note the "Shared" here
Dim Name As String
Dim Street As String
Dim ID As Integer
Sub Show()
MsgBox("Address: " & Name & ", " & Street)
End Sub
End Class
Let's look at the life of our application:
1. Right after start, the memory for the shared field "LastID" is automatically
reserved. It's an Integer, so it's 4 bytes. No object has been created yet.
2. Using the expression "new Address" once, the first instance
of our class is created. Now there is the shared field plus one object
in memory.
3. Another use of "new Address" creates the second object.
Now there is the shared field plus two objects in memory.
As a calculation:
memory for shared fields
+ count of objects * memory required per object
---------------------------------------------
total memory consumption
In other words: The shared fields always exist exactly one time, whereas there
are zero to infinite instances of the Class.
Next step: let's make use of the Shared field in order to make it clear.
Before, we've learned how to access members of an object by using the
(abstract) expression
VariableName.MemberName
Knowing that the variable references an object, you should now ask:
Does it make sense to access a _shared_ member using the same syntax?
No, it doesn't. Why? Because the shared members belong to the type,
not to the object. Consequently, a shared member is not accessed
by writing down the variable name referencing the object but by
writing down the type name. In our example:
MsgBox(Address.LastID) 'Outputs the content of the shared field in a message box
So, the general syntax is
TypeName.FieldName
That's how it works. There is nothing more to say about it. I can
only give a useful example to make it clearer. So, let's create
our first task:
You can imagine that in a real environment we don't have consecutive
lines creating one object after the other, so that it is obvious that
they get the IDs 1, 2, etc like in our example. A real life task is
creating a new Address object with a new ID for example by clicking
on a button. Don't worry, I'm not gonna talk about Button-clicks
here.
If you analyze the task you will notice that we must remember the
ID that we've used for an Address in order to know which ID the
next object will get. This we have to do even if we write code the
old-fashioned way. Now comes the Shared field named "LastID" into
play: Yep, that's where we store the last ID assigned to the Address
object created at last. So, let's make use of it:
Dim TheAddress As Address
Dim NewID As Integer
NewID = Address.LastID + 1 'Retrieve the last ID and calculate the new one
Address.LastID = NewID 'Write back the new ID to be used in future
TheAddress = New Address 'Create new Address object
TheAddress.ID = NewID 'Assign the new ID
TheAddress.Name = "Strand"
See the comments for explanation. Does it make sense? Again, remembering
the last ID is not a new technique, it's just where it is stored: in a
Shared field. Again, the advantage of putting it into the class is that
these things belong together. It's the first step to what is called
"encapsulation", which is also a fixed term. It is the basis to further
techniques that give the whole thing even more sense, but I don't
elaborate on these techniques. For you it's just important to know that
it's a way of grouping associated things together. The last ID is
related to this class and therefore it's a good place to store it there.
Ok, back to our final target again: By now you only know what a "shared field"
is. But what is a "shared method"? This was the initial question. You may
anticipate it already: A shared method is a method that is not related to an
object. In a shared method, no instance of the class (= no object) can be
accessed. Inside a shared method, you can only access the shared fields
of the class. I give you an example:
We were talking about code reusage. Code must be designed as abstract
as possible to be able to reuse it. This is to save time. I think this
always makes sense. You already know how to write reusable code:
Write Subs and Functions to avoid writing the same code multiple times
at different locations. Let's have a look at the latter code above:
It does a nice job in creating a new object with a new ID. This is
description predestinates the job to be put into a Function. The
Function does it's job and returns the new object with the new ID:
Function CreateInstance() As Address
Dim TheAddress As Address
Dim NewID As Integer
NewID = Address.LastID + 1 'Retrieve the last ID and calculate the new one
Address.LastID = NewID 'Write back the new ID to be used in future
TheAddress = New Address 'Create new Address object
TheAddress.ID = NewID 'Assign the new ID
TheAddress.Name = "Strand"
CreateInstance = TheAddress 'The function return value is the reference to the object
End Function
The only new thing here is the line "CreateInstance = TheAddress".
Though, you already know how to return a function value. It's the
same with object references, so it's not really new.
Having this new Function, we can modify our main procedure:
Dim Address1 As Address
Dim Address2 As Address
Address1 = CreateInstance()
Address2 = CreateInstance()
It works like you already know it: The function returns a value.
The value is stored in the variable. Be the function return type
an Integer, a Single or an object reference, it's always the same.
You may have noticed that I have concealed something: Where do
we put the Function? For the explanation it was "just there", but
now we need a home for it. I hear you saying: yeah, yeah, let's
make it a Shared Function! Ok, I do as instructed and explain
all the changes below:
Class Address
Dim Shared LastID As Integer
Dim Name As String
Dim Street As String
Dim ID As Integer
Sub Show()
MsgBox("Address: " & Name & ", " & Street)
End Sub
Shared Function CreateInstance() As Address '<--------- Shared keyword added here
Dim TheAddress As Address
Dim NewID As Integer
NewID = LastID + 1 'Retrieve the last ID and calculate the new one
LastID = NewID 'Write back the new ID to be used in future
TheAddress = New Address 'Create new Address object
TheAddress.ID = NewID 'Assign the new ID
TheAddress.Name = "Strand"
CreateInstance = TheAddress 'The function return value is the reference to the object
End Function
End Class
In addition to adding the Shared keyword, I've changed
NewID = Address.LastID + 1
Address.LastID = NewID
to
NewID = LastID + 1
LastID = NewID
Both versions do exactly the same. The reason why "Address." can be dropped
is that the code is already _inside_ the Class Address. As explained
before, name resolution is always done inside out. This also applies
to shared member names: If there is no local variable with the name,
the fields of the Class are sought. So, let's analyze this line:
NewID = LastID + 1
- The name "NewID" is a local variable. It's used as the destination
of the assignment.
- The name "LastID" is not found as a local variable, but it is
found as a Shared field.
So, the reason for putting the Function into the class is that it makes
sense, another time. You must think a little ahead to see the advantage:
We often have to use the same Class in different projects. If everything
related to the Class is put inside, we just have to take the Class
as a whole because everything required is already in it. The old-fashioned
way you'd have some parts of it outside, so you have to collect things
together first before you have all you need. Comprehensive?
The only thing what's missing for our goal is how this beast of a Class is
used now:
Dim Address1 As Address
Dim Address2 As Address
Address1 = Address.CreateInstance()
Address2 = Address.CreateInstance()
You see that the syntax of accessing a shared method is not different
from accessing a shared field. So we can write down the general syntax
for accessing shared members:
TypeName.MemberName
Done! That's it. You have now the theory to follow the instruction:
"Put the file access into a Shared method in a Class.
For this purpose, you first have to know what a Class
and a Shared method is."
Even if you know some basics now, it's only the beginning. Therefore,
I think it's not yet convincing that these efforts will pay later.
To believe it, you'd have to know a few more things. I'll continue
trying to convince you whenever you are ready. And again, please
ask if something is unclear.