Introduction
One of the challenges of working with objects is returning property values,
which you have changed, to their original state. This can mean writing the same
lines of code again and again in any functions which change properties, that
must be returned to the same value they possessed before your code "dirtied" the
object.
This article presents what I feel is a reasonable solution that can be
applied to C++, C#, VB or VB.NET, and can be understood by developers of all
experience levels. For the beginner it presents an introduction to objects and
issues related to object lifetime. For the advanced developers it shows us how
we can take advantage of certain behaviours to save significant amounts of
coding effort, and retain a uniform approach to protecting the values of objects
which are shared amongst various processing sub-routines.
The Approach
The approach is very simple, it involves creating a simple class whose
constructor retains a reference to an object and then moves on to preserve known
property values, that the developer wishes to protect. I refer to such objects
as the Protectors
.
A Protector
is any class which
protects the values of another object such that when the Protector is destroyed,
the object which is being protected has its property values reset to their
initial values, which were in use at the time the
Protector
was created.
A Protected
Object is an object whose property
values can change and subsequently be automatically restored, in other words it
is guarded by a Protector
.
When a Protector
is instantiated the
object requiring protection is passed as a reference to the
Protector
, together with values which
are to be assigned to properties of the
Protected
Object.
This approach works in object orientated language as instatiation and
destruction of objects automatically causes known sub-routines to be executed.
These known sub-routines are often referred to as Constructors and Destructors.
In Visual Basic, they are known as SUB NEW
and
SUB
FINALIZE
.
Words of Caution
The behvior of OO languages when creating and destroying objects differs
widely. For example, in C++ when you destroy an object its destructor is called
immediately. This is not the case in VB.NET (see the documentation on
FINALIZE
for an explanation). Therefore the downloadable example is
a moredetailed and accurate demonstration of how to implement
Protector
objects, as they are
described below.
This means that two lines of code are required to utilise
Protector
objects, the first being to
create a Protector
and the second to
Dispose it.
Overview of Example
The example offered is described by the use of VB.NET code, but translation
to C++ or C# is a trivial matter.
The SUB NEW
for the
Protector
accepts two parameters, the first the object reference to
the Protected
Object and the second
parameter is a value which is assigned to a property of the
Protected
Object.
Therefore, in the example code below, the only two methods available on the
Protector
are the
Sub New
and
Sub Finalize
sub-routines. The Sub New is effectively
called by the developer creating a new
Protector
, whilst VB.NET will
automatically call the Sub Finalize routine when the
Protector
object is destroyed.
The Lifetime of the Protector
The lifetime of the protector in the example is the scope of a single
sub-routine which utilises the
Protector
object. That is to say that, when the sub-routine begins the
Protector
is created, and when the
sub-routine ends the protector is automatically destroyed, a process often
referred to as "going out of scope".
The approach can be used within any scope though, eg: a
Protector
can exist at module level,
the developer can also create and destroy protectors at will using the syntax
NEW and MyProtector = Nothing, effectively creating a new
Protector
then destroying it.
Example Code
The following example code sections provide a basic outline of Protectors. In
reality, writing a protector is more involved, however, the pay offs can still
be significant.
Please download and study the sample code for an accurate implementation of
the Protector
concept in VB.NET.
The purpose of this example is to protect the
SmoothingMode
property of a
GraphicsObject
, such that the developer can
change SmoothingMode to any value during the course of a sub routine and have
SmoothingMode
returned to its original value when the sub-routine
completes.
Public Class GraphicsProtector
Dim mGraphics as GraphicsObject
Dim mSmoothingMode as SmoothingMode
Sub New(ByRef prObject as Graphics, ByVal pvNewMode as SmoothingMode)
mGraphics = prObject
mSmoothingMode = mGraphics.SmoothingMode
mGraphics.SmoothingMode = pvNewMode
End Sub
Protected OverRides Sub Finalize()
mGraphics.SmoothingMode = mSmoothingMode
End Sub
End Class
The preceeding code describes a simple class which protects the
SmoothingMode
property of the specified
Graphics
object. The following example now illustrates how the
Protector named
GraphicsProtector
operates to ensure that the
Graphics
object
SmoothingMode
property is reset to its initial value when the
sub-routine completes.
Sub DrawSmoothRectangle (ByRef prGraphics as Graphics)
Dim lProtector As GraphicsProtector = New GraphicsProtector( _
prGraphics, SmoothingMode.HighQuality)
prGraphics.DrawRectangle(....)
prGraphics.FillEllipse(...)
End Sub
The preceeding code is an outline which illustrates the kinds of methods
which may benefit from having graphical smoothing effects applied. For instance,
when prGraphics
is passed into the sub-routine
DrawSmoothRectangle
it could have a value of
SmoothingMode.HighSpeed
, which may not give the desired graphical
quality, therefore the sub-routine changes this to
SmoothingMode.HighQuality
, but the developer would then have to
ensure this property is returned to its original value of
SmoothingMode.HighSpeed
, else risk slowing down all other drawing
processes which occur after this sub-routine completes, something which is very
easy to forget and causes all maner of problems.
Using this approach, the developer no longer needs to remember to restore the
value. Only one line of code is required to switch the property to its new value
and have the original preserved.
The Chain of Events
DrawSmoothRectangle
is passed a Graphics
object
into which the developer can draw miscellaneous shapes.
- The developer switches the
SmoothingMode
to the desired
quality, and has the original value preserved by the
GraphicsProtector
object
- The developer proceeds to draw.
- The
GraphicsProtector
object then goes "out of
scope", i.e. it is automatically destroyed when the sub-routine ends.
- At the time that the
GraphicsProtector
goes out
of scope it restores the value of prGraphics.Smoothing
mode to its
original value.
For clarfification; rather than letting the system the destroy the object
automatically at the end of the DrawSmoothRectangle
routine, the
developer could have written
lProtector = Nothing
This too would have destroyed the GraphicsProtector
object
(lProtector
), but the whole point of this approach is to write as
little code as possible to achieve the maximum amount of housekeeping.
Advancing the Concept
From this simple start point the Sub New
, or constructor, can be
modified to accept values for numerous properties so that in a single line,
multiple properties are preserved and assigned, which affords the developer
great relief from repetitive coding.
Protectors can be instantiated and destroyed at will, not just at the top of
sub-routines. Any time the need arises to protect some values whether its on the
first or tenth line of a routine a Protector can be instantiated and manually or
automatically destroyed.
Other examples of protectors are remembering the size and position of
windows. The names, styles and sizes of fonts in use on a form. The style of the
mouse cursor, all of which are things that the developer could change, and if
not aware of the impact of such changes on successive functions erroneous
conditions can arise within an application.
Conclusion
Utilising the automatic behaviour of object orientated systems provides great
leverage. With a little thought elaborate schemes can be concocted from very
simple techniques, the result being that small amounts of code can achieve
immensely useful results, be those results measured in time saved in
developement, or in later maintenance and support of code written using such
techniques.
PLEASE NOTE: You must utilise Protectors as completely depicted in the
example source code, otherwise you will experience very strange errors, which
occur due to the .NET system disposing your objects at its discretion, as
opposed to when you ACTUALLY want them disposed. (Thanks to Ian Darling for his
reminder on this very important issue.)
I thank you for reading this article and for taking time to score my efforts,
such that I can measure my contribution to this forum, from which I have gained
immensly useful examples, and contact with people whose oppinion I hold in very
high esteem.
English guy now living in Australia. Developed in VB/C++ and assembler for many years. Just begun working with .NET. Keen to develop many resuable components. Have great system at home.