There's something that's always bothered me a little bit about creating classes. We go to the trouble of creating backing Private fields and Public properties in order to implement data hiding - right? And when we want to include data validation, we usually use properties for that task, don't we?
But then what do we do? We go and create a parameterized constructor that lets client code push invalid data straight into a field. At least that's what I see in most of the class building examples I've come across - both in books as well as in real world code. It's quite rare to see values passed into properties in a parameterized constructor; fields seem to be the most popular recipients of initialization data.
I'm surprised at how many times I've seen this done. Of course it's possible to include validation in the constructor too (but the examples I've seen that use fields rarely seem to do this; leaving the door to bad data wide open)
Luckily, we now have a very easy solution - Object Initializers.
In fact, you could claim that with the arrival of Object Initializers the parameterized constructor has just about become redundant.
Parameterized constructors were devised to make it easy to pass arguments in to a constructor and to avoid having to add several lines of code for property values when initializing a new object. You know the sort of thing:-
Dim sr As New SalesRecord
sr.CustomerName = "Joe Soap"
sr.Total = 2399.65
' etc etc
which was short-circuited with :-
Dim sr As New SalesRecord("Joe Soap", 2399.65)
However, as I said, in most cases people code this so that it assigns the CustomerName and Total values directly to the fields.
Object Initializers step round this issue while still enabling client code to be fairly concise. How it works is that when you initialize an instance of a class object you are allowed to use the 'With' keyword as a way of assigning values to properties, all in one line of code.
Let's knock up a quick class to demo this.
Public Class Product
Private _itemcode As String
Public Property ItemCode() As String
Get
Return _itemcode
End Get
Set(ByVal value As String)
_itemcode = value
End Set
End Property
Private _cost As Decimal
Public Property Cost() As Decimal
Get
Return _cost
End Get
Set(ByVal value As Decimal)
If value > 0 Then
_cost = value
Else
_cost = 0
End If
End Set
End Property
Private _itemname As String
Public Property ItemName() As String
Get
Return _itemname
End Get
Set(ByVal value As String)
_itemname = value
End Set
End Property
End Class
So, three fields, three properties (one of which includes some validation) and just the default constructor. To create a new instance of the Product class and assign values to any of the properties that interest us, this is what we can now do:
Dim P1 As New Product With {.ItemName = "Widget", .Cost = 0.99}
That's it! A Product instance with those values for the ItemName and Cost properties now exists. And of course the validation is applied effectively at the time of instantiation to avoid any unacceptable values being passed.
Note that the property assignments are contained inside curly braces and not standard parentheses.
There are several advantages to this approach. Not only does it get around the validation problem I mentioned at the start, but it also allows you to pick'n'mix the properties you want to assign values to when you instantiate the object. As you saw in my example above, I chose only to assign values to two of the properties.
And if that's not enough to get you interested, there's more. You even have the further advantage that you can pass the values in any order you like (unlike the standard parameter arguments which have to be passed in a strict order).
Finally, if there is a parameterized constructor available that contains some, but not all, the arguments you want to pass in, then you can simply combine the two approaches and use the constructor's parameters, as well as the object initializers, all in one chunk of initialization code.
So if you had this parameterized constructor in the class:-
Sub New(ByVal code As String)
Me.ItemCode = code
End Sub
you could use the following code to assign values to all three properties:-
Dim P2 As New Product("TOOL12") With {.ItemName = "Spade", _
.Cost = "12.99"}
How cool is that?
To be honest, I'm not sure how often I'd want to do that - given that I can do the same thing using three object initializers - but it's an option to consider.
So there it is. A new feature that you may have heard about but weren't sure if it was going to be of use to you. Personally, I think it's great and plan on implementing this approach whenever I can from now on.