KnowDotNet

Building Better Properties

...a case for using them

by William Ryan


So why use Properties over Public Variables?

The first reason is really simple - you don't break encapsulation.  This sounds rather trite but if you think about it for a minute, it's really important.  If you write object libraries, then it's absolutely critical.  Why?  Well, let's use a common example of a control that we are all familiar with, the
Windows.Forms.TextBox control.  The most common property that you use with it is probably the .text property.  The .enabled and .visible properties are two other common ones.  Well, it's pretty common to use the TextChanged event to validate your input and often times enable other controls based on its validity (there are other ways to accomplish this, but I'm just trying to use a common example).  Now, if .text were declared as a public property, how could you trap the textchanged event?  Well, you could trap KeyDown or some other event but those are all based on properties as well.  So if you hade only public variables instead of properties, guess what?  You wouldn't have events.  That would make programming a little less fun wouldn't you agree?  So what happens behind the scenes?  Why is this so?  Well, it's pretty simple.  In any given class, you can define any event you want.  Even if you had public variables, you could still have events, like TextChanged.  The problem stems from the fact that without properties, how would you raise this event from inside your class?  Since encapsulation is broken, outside entities can change the value without the permission of a gate keeper (the property).  So how could you know it changed from inside the class?  Trust me on this, any solution, and I've heard some pretty intersting ones, is way more trouble than it's worth, and even if it works, will be very coupled with the variable.  However, in the textbox example, every time a call to the Set method of the property is made, you can raise an event, TextChanged, inside your class.  Then, whereever the textbox is declared, or your object, you can write an event handler and viola' you have events and handlers.

The next reason is validation of data.  If you use a public variable instead of a property, how can you validate it?  The common response you'll hear is that you can validate it in the caller.  This only has any validity if you are the only one that uses your class(es) but that means you are going to have to write a lot of unnecessary validation code and no one can use your class without knowing the inner workings of it.  That kind of goes against the entire grain of OOP don't you think?  Even if you don't mind writing the extra code, you'd have to agree that you wouldn't want to use someone else's classes that you had to guess about appropriate values and guard against invalid values every time you call it?  Still not convinced?  Well, if a ListBox's Item property was implemented as an Integer and as a public variable, you could set item -32,600 and you'd never know that was a problem until someone tried it and your control blew up.  And who would do that?  Idiots?  Well, them for sure.  So would newbies, people who made a typo, and advanced programmers trying to learn more about your class.  And what are they going to find out?  That your code is buggy.  Think of how many 'bugs' and shortcomings people find in VB.NET or C# just by people trying to do things in ways that weren't intended.  And trust me, when those are discovered no one ever says anything nice about it.

The next problem comes when you'd need to use indexed properties.  Take a ComboBox for example.  It has a Items collection right?  So how would you implement this same functionality with a Public Variable?  The typical answer you might here from a public variable advocate is something like "I'd declare it as an Arraylist or Array"...and even more likely one is "Why would you want to do that?"  So let's go with the first one.  We'll add an Arraylist and make it public.  What happens when a programmer tries to add a DataReader to your collection when it was expecting a string?  So you go away from this method and use an Array.  After all, they are clearly typed right?  Ok, so how do you overload that?  Well, let me answer that for you....know matter how much code you write, you are't going to overload a variable.  And one last thing on this subject, how do you make a public variable the default property or default indexed property of your class.

If nothing else, I hope I've got you to think about properties in a different way.


Default Properties?

One of my favorite examples of a well implemented default property is the DataReader (take your pick of favorite flavor)  
Item property (not the Item variable ;-) ) If I have a System.Data.SqlClient.SqlDataReader named 'dr', and I'm in the middle or a .Read loop, I can reference the first Item one of two ways.....  

While dr.Read
    Debug.WriteLine(dr(0))  'OR
    Debug.WriteLine(dr.Item(0))
End While


Other the fact that I didn't strongly type my reference, this is pretty cool isn't it?  Since Item is the main property you reference with DataReaders, giving it a default value allows a developer to skip the .Item reference.  In order to declare a property as Defualt, you have to do something really difficult.  You need to add the word Default before the word Property in your class.  And no, it doesn't work with public variables.

So, to make this work, you'd do this:

Defualt Property SomePropertyName(ByVal someIndex as Integer) as WhateverType


Instead of this:

Property SomePropertyName(ByVal someIndex as Integer) as WhateverType


There's are some  important caveats regarding default properties.  You can only define Indexed Properties as Default.  In VB6 this wasn't the case.  Think about the label and textbox controls. In VB6 you could write myControl = "My Control" but in the .NET languages, the .Caption property isn't indexed, therefore not default.  In addition, you can't declare Shared or Private Properties as Default.  Finally, you can only have one default property per class.  This makes sense after all, how else would the developer or compiler know what you wanted to do?  

So how do you create these guys?

Class Programmer
  Private _Languages as HashTable

  Public Sub New()
      'By Instantiating it here, we only have to reserve memory for members that we intend to use
      _Languages = New HashTable
  End Sub

  Public Default Property Languages(ByVal Item as Integer) as String
        Get
              Return Ctype(_Languages(Item), String)
        End Get
          
        Set(ByVal Value as String)
              _Languages(Item) = Value
        End Set
  End Property

End Class


Shared Members are really cool!

Ok, if you came from a C derivative or Java, you are used to public static void main...the main word(no pun intended) being static.  In C# static members are the equivalent to VB.NET shared members.

There's one very good reason to use these - you don't need to instantiate an object in order to call the method or get a value.  For utility classes, ones that are very generic and suited to common tasks, you definitely want to take advantage of these.  Think about this for instance:  MessageBox is a class and Show is a shared/static member.  How many times do you call MessageBox.Show in a typical program?  Can you imagine if you had to instantiate a MessageBox object each time you wanted to use it, and then dispose of it?  Would that be efficient in any way shape or form?  What about the Math classes?  Think about how many utility classes you probably have in your own library that don't need to be instantiated?

The only real catch is this.  A shared member can't reference a instance variable.  Don't forget this.  However, if you want to still preserve encapsulation while using Shared properties, simply declare the private member as Both PRIVATE AND STATIC.


Class DataHandler

   Private Shared cs as String = "Your connection string"
   Private Shared cn as System.Data.SqlClient.SqlConnection
   Private Shared cmd as
System.Data.SqlClient.SqlCommand

   Public Sub New()

   cn = New SqlConnection
   cmd = new SqlCommand
   End Sub


  Public Shared Function GetMyData(ByVal sql as String) as SqlDataReader
       'Blah blah blah

       Return myCommand.ExecuteReader
     End Function

End Class


Then you can simply call this by using:

myDataReader =  DataHandler.GetMyData("SELECT * FROM myTable")

Instead of:

Dim dh as New DataHandler()

myDataReader = DataHandler.GetMyDate("SELECT * FROM myTable")

Real Classes Raise Events!

Well, that's not necessarily true but I needed a good lead in.  You may have remembered my little tirade about how lame public properties are.  So I guess if I'm going to run my big mouth like that, I should probably show an example to support my argument.

Here goes...

Imports System
Imports System.Collections
'Add the rest up here


Public Delegate Sub ConvenienceStoreEventHandler(ByVal Source as _
          CoolConvenienceStore, ByVal _
          e as CoolConvenienceStoreEventArgs)


Public Class CoolConvenienceStore

        Public Event BoughtADietCoke as ConvenienceStoreEventHandler
        Public Event BoughtLotteryTicket as ConvenienceStoreEventHandler

        Private _DietCoke as String
        Private _LotteryTicket as String

        Public Sub New(ByVal i as ActionEnum)

    Select Case i
        Case ActionEnum.BuyDietCoke
          _DietCoke = "Thank You, Come Again!"
        Case ActionEnum.BuyLotteryTicket
         _LotteryTicket = "You can't lose if you don't play"
    End Select

        End Sub
     Public Sub Purchase(ByVal i as ActionEnum)
    Select Case i
       Case ActionEnum.BuyDietCoke
                RaiseEvent BoughtADietCoke(me, myEventArgs)
               Case ActionEnum.BuyLotteryTicket
         RaiseEvent BoughtLotteryTicket(me, myEventArgs)
       Case Else
       'Do nothing, we only want to validate these two events
            End Select

     End Sub

End Class


Now this class is a little silly, but that's the way I intended it.  The constructor is where we actually initialize the private members and in reality, a non-trivial class will have multiple overloaded initializers in most instances.  Moreover,the ActionEnum doesn't need to be passed in two places,  like it was, but I was trying to emphasize a point.  Finally, I used the ActionEnum to raise the event.  Normally though, this is how we'd do our validation.  Instead of buying a DietCoke, we'd make sure there was a valid member passed in and yell if not.  Anyway, hopefully this will get you thinking a little more about properties.

Oh Yes, one Last thing I just came across.  If you are using the security classes in your code...you can't protect public memebers, only properties and methods.  I just came across this and will add more in one of my next articles.