Article Options
Premium Sponsor
Premium Sponsor

 »  Home  »  .NET Newbie  »  OOP Basics - Property Validation and Exceptions
 »  Home  »  Visual Studio 2008  »  OOP Basics - Property Validation and Exceptions
 »  Home  »  Windows Development  »  OOP Basics - Property Validation and Exceptions
OOP Basics - Property Validation and Exceptions
by Ged Mead | Published  11/07/2009 | .NET Newbie Visual Studio 2008 Windows Development | Rating:
Ged Mead

Ged Mead (XTab) is a Microsoft Visual Basic MVP who has been working on computer software and design for more than 25 years. His journey has taken him through many different facets of IT. These include training as a Systems Analyst, working in a mainframe software development environment, creating financial management systems and a short time spent on military laptop systems in the days when it took two strong men to carry a 'mobile' system.

Based in an idyllic lochside location in the West of Scotland, he is currently involved in an ever-widening range of VB.NET, WPF and Silverlight development projects. Now working in a consultancy environment, his passion however still remains helping students and professional developers to take advantage of the ever increasing range of sophisticated tools available to them.

Ged is a regular contributor to forums on vbCity and authors articles for DevCity. He is a moderator on VBCity and the MSDN Tech Forums and spends a lot of time answering technical questions there and in several other VB forum sites. Senior Editor for DevCity.NET, vbCity Developer Community Leader and Admin, and DevCity.NET Newsletter Editor. He has written and continues to tutor a number of free online courses for VB.NET developers.

 

View all articles by Ged Mead...
Validating A Property

In an earlier article in this series on OOP Basics, I showed you how to create some simple fields and properties. The code for these was as follows:

Code Copy
    Private m_forename As String = "No Forename"
    Public Property Forename() As String
        Get
            Return m_forename
        End Get
        Set(ByVal value As String)
            If Len(value) < 8 Then
                m_forename = value
            Else
                m_forename = "Too long"
            End If
        End Set
    End Property

    Private m_surname As String = "Anonymous"
    Public Property Surname() As String
        Get
            Return m_surname
        End Get
        Set(ByVal value As String)
            m_surname = value
        End Set
    End Property

    Private m_dateofbirth As Date = #1/1/1900#
    Public Property DateOfBirth() As Date
        Get
            Return m_dateofbirth
        End Get
        Set(ByVal value As Date)
            m_dateofbirth = value
        End Set
    End Property

    Private m_gender As String
    Public Property Gender() As String
        Get
            Return m_gender
        End Get
        Set(ByVal value As String)
            m_gender = value
        End Set
    End Property


These Properties will work fine in an ideal world where no-one ever tries to enter invalid values for data. However we all know that in the real world if something can go wrong, it will.

So I next want to look at some ways of improving the resilience of properties.

Firstly, it would be useful to validate the DateOfBirth property to prevent a future date being entered in error. This kind of validation is a very common requirement and is easy to implement in a Property Procedure. This is another reason why it is often worth going to the extra trouble of creating Public properties and Property Procedures.

Once the validation has taken place, we will also look at how you can throw Exceptions if client code tries to misuse our class. Finally, we will review and improve the user experience if and when an Exception is raised.

The DateOfBirth Property
A good example of how things can go wrong is the DateOfBirth property shown above. Without some kind of check on the value that is passed in, it will be possible to assign a date in the future, for example. Clearly this would make no sense where dates of birth are concerned.

Validation
As you can see from the code snippet at the start of this article, the value of the date of birth can be assigned or changed via the Set sub block of the DateOfBirth property procedure. Client code accesses the Public property and the values are "exchanged" at that point, in the sense that a corresponding value is stored in the Private m_dateofbirth field inside the class.

Logically then, the Set block is where we should place validation code that will stop client code from Setting a date that has not yet arrived. Our first pass at the validation code is fairly simple:

Code Copy
    Private m_dateofbirth As Date = #1/1/1900#
    Public Property DateOfBirth() As Date
        Get
            Return m_dateofbirth
        End Get
        Set(ByVal value As Date)
            If Date.Compare(value, Now) < 0 Then
                m_dateofbirth = value
            End If
        End Set
    End Property

In case you're not familiar with the Compare method of the Date Class, the above code:

  1. Takes the date that has been passed in from client code (Value).
  2. Compares this to today's date.
  3. If it finds that the first date (the date being passed in by client code) is earlier than today's date it allows m_dateofbirth to be changed to this new date.
  4. If it finds that the date being passed in is a future date then it does nothing.

Here is some sample client code which tries to assign a future date:

Code Copy
        '  Create a Person with a future birth date
        Dim UnbornPerson As New Person("Jo", "Snooks", #11/11/2020#, "Male")
        '  Display the result
        Label1.Text = UnbornPerson.ToString & _
        vbCrLf & UnbornPerson.DateOfBirth.ToLongDateString

The resulting output will look like this:

  

You'll see that the new Person has been assigned a date of birth of 1st Jan 1900. This happens because the value being set was rejected and so the default value that we set on the m_dateofbirth field is used.

Code Copy
    Private m_dateofbirth As Date = #1/1/1900#

This default value isn't really intended to set any Person instance to this arbitrary date. Its main purpose is to avoid there being no date at all applied, which could cause the application to crash in some circumstances.

In fact, what would be much more useful to users of our class - client code - is some kind of feedback when an invalid value is passed in. One way of doing this is to use Exceptions.

Exceptions
You'll be a very lucky .NET programmer indeed if you haven't already seen more of these things than you really wanted to:

    

You will get these kind of Exception messages at run time when you have written code that the Framework isn't able to handle. But perhaps you haven't yet stood on the other side of the fence and actually thrown an Exception of your own? This is something that as an OOP-savvy developer and creating classes of your own, you should consider doing.

Actually, there's very little to it. In this situation, where we want to tell developers who use our class that something unacceptable or exceptional has happened, we can simply throw a suitable Exception to make them stop and think. We leave the choice of what they do about it to them. Our part of the task is simply to flag it up for their attention. What you should always try and do, though, is give them enough useful information so they can understand and hopefully rectify the problem.

Here is the amended version of the DateOfBirth property:

Code Copy
    Public Property DateOfBirth() As Date
        Get
            Return m_dateofbirth
        End Get
        Set(ByVal value As Date)
            If Date.Compare(value, Now) < 0 Then
                m_dateofbirth = value
            Else
                Throw New ArgumentOutOfRangeException _
                ("Date of Birth", _
            "A future date has been entered for the Date of Birth.")
            End If
        End Set
    End Property

With this in place, if we once again run the client code:

Code Copy
        Dim UnbornPerson As New Person _
        ("Jo", "Snooks", #11/11/2020#, "Male")
        '  Display the result
        Label1.Text = UnbornPerson.ToString & _
        vbCrLf & UnbornPerson.DateOfBirth.ToLongDateString

The Exception will be thrown and the user will understand why the entry isn't accepted.



In the example shown above where the project is still being developed in Visual Studio, the 'user' would probably be you as the developer. But once the full user interface is built, other external users of the compiled application will be able to enter dates and the Exception message will appear as a normal Windows MessageBox if they enter a future date.

It's important to understand that at this point neither m_dateofbirth or DateOfBirth are changed. As things stand, m_dateofbirth will still have whatever value it held prior to this snippet of code being run. That may be the default value if this is a new, unchanged instance or may be a previously entered value that was acceptable.

Handling The Exception
While it is useful to be able to validate and throw an Exception if the validation fails, the running application will still come to a halt if this error is made. The user will see the following:

While this is better than nothing, it will still scare a lot of users and a more user friendly approach would be to use a Try Catch Block which will trap this Exception and give the user some clear feedback on what to do.

Here is a better version of the client code:

Code Copy
        '  Try to create a Person with a future birth date
        Try
            Dim UnbornPerson As New Person _
                   ("Jo", "Snooks", #11/11/2020#, "Male")
            '  Display the result
            Label1.Text = UnbornPerson.ToString & _
            vbCrLf & UnbornPerson.DateOfBirth.ToLongDateString

        Catch ex As ArgumentOutOfRangeException
            MessageBox.Show("You have entered a future date." _
               & vbCrLf & _
               "Please re-enter a valid date", _
               "Warning", _
               MessageBoxButtons.OK, _
               MessageBoxIcon.Exclamation)

        End Try

When the same client code which contains the invalid date is run, the user will see a more useful and less threatening message format:

  

In this short article, we have looked at an example of property validation. You can of course add further validation requirements to the DateOfBirth property - perhaps not accepting a DateOfBirth less than 10 years earlier than today's date. You may also add validation to other properties on the class.

End users are not likely to understand what an "ArgumentOutOfRangeException" is and won't be impressed if that is all the information they are given. So running validation, throwing an Exception in the class code and using a Try Catch block to handle it is a good way of making your code as robust as possible. At the same time, when input errors do occur you have created a means of keeping the user informed of what the problem is and how to correct it.

How would you rate the quality of this article?
1 2 3 4 5
Poor Excellent
Tell us why you rated this way (optional):

Article Rating
The average rating is: No-one else has rated this article yet.

Article rating:3.98701298701298 out of 5
 77 people have rated this page
Article Score8899
Sponsored Links