Alright, so I’ve spent a lot of my time this summer (and more this fall) going to
various groups and talking about the features in C# 3.0. This is something of
a thorn in the side of my good friend Cory Smith,
who despite the fact that he is a “Visual Developer” MVP, is the staunchest Visual
Basic advocate that I’m aware of. I swear this man dreams VB, I know he blogs
VB over at AddressOf.com. As such, I thought
it was time to give all of my friends who prefer Visual Basic their fair time, or
at least some time, so this post details the new features and shows the syntax for
things. There is not exact parity to the C# feature list, but its a close thing,
so here we go.
Automatic Properties
We’ll start of with the biggest exclusion. VB does not get automatic properties,
so they are left with the more verbose version of the syntax.
Partial Methods
Partial Methods are very useful for light weight event handling in generated code.
For instance, lets assume that you had a generated class that looked like this:
Partial Class NewFeatures
Partial PrivateSub BeforeTextChange(ByVal oldText AsString, ByVal newText AsString)
End Sub
Partial PrivateSub AfterTextChange(ByVal oldText AsString, ByVal newText AsString)
End Sub
Private _text AsString
Public Property Text() AsString
Get
Return _text
End Get
Set(ByVal value AsString)
BeforeTextChange(_text, value)
_text = value
AfterTextChange(_text, value)
End Set
End Property
End Class
The
partial methods BeforeTextChange and AfterTextChange are convenient extension points
should the person using your generated code needs to hook into them, but the magic
is that any of these methods which are not given implementations (both in our case
above) get compiled away.On the right you can see the Reflector disassembly
for Text property. Note that there are no calls to the partial methods, because
the compiler did not emit IL for them since there were no implementations.
Now let’s add the following short code-snippet into our project:
Partial Class NewFeatures
Private Sub BeforeTextChange(ByVal oldText AsString, ByVal newText AsString)
newText += oldText
End Sub
End Class
With
this added, we can go examine the Reflector disassembly again, and this time we see
something very different. Now we see the call to BeforeTextChange but still
no AfterTextChange because it still has no implementation.
Now, some important notes about this feature. Partial Methods must be Subs and
they must be Private. This is to avoid them being compiled away either changing
your interface (private) or breaking other code which depends on its results (hence
no functions).
Implicitly Typed Variables
Implicitly typed variables are not variants, this is very important so I’ll repeat
myself, Implicitly typed variables are not variants. These are strongly typed
variables which you simply forgo the need to type (as in press keys) the type (as
in the class name).
The restrictions? You must initialize the object on the same line it is declared,
so that the compiler can infer the type. Here is an example:
Dim myString = "This is a string, I swear"
Now myString is a System.String, not a System.Object or anything else.
Object Initializers
This is a minor tweak to something which VB has always been better at than C#.
It makes a more compact version of the With syntax, at the cost of adding curly braces
into VB, so you can judge if it’s worth it for yourself.
Sub Main()
Dim ds = New SqlClient.SqlConnection() _
With {.ConnectionString = "Blah"}
End Sub
For C# this feature is a god send, but C# didn’t always have With, so it’s your call
if this is truly a great feature.
Anonymous Types
Now, the addition of With as part of an object initializer is a little ho-hum, but
this is not. Enter Anonymous Types, a strongly typed object where the type is
not explicitly declared, but rather implicitly declared.
Sub Main()
Dim ds = NewWith {.FirstName = "Tim", _
.LastName = "Rayburn"}
End Sub
Ok,
have I blown your mind yet? No? Well, then try this in for size… On the right is
the Intellisense you receive when you type “ds.” with this variable in scope.
The compiler has converted out simple syntax above into a full type with two properties
: FirstName and LastName. Pretty darned sexy if you ask me.
Restrictions? Of course there are restrictions. These can only be used
as private or local variables. If any other type will ever see this object them
you have to convert this to a full class. The VB compiler will let you make
these public, but it emits the type as System.Object and you loose intellisense.
Embedded Xml
This is a feature that C# 3.0 does not have, it is solely the province of Visual Basic
and I must admit, it kind of rocks. But it will also break your head the first
time you read it:
Sub CreateCityXml(ByVal name AsString, ByVal lat AsDouble, ByVal lon AsDouble)
Dim cityXml As XElement = _
<City>
<Name><%= name %></Name>
<Latitude><%= lat %></Latitude>
<Longitude><%= lon %></Longitude>
</City>
cityXml.<City>.<Longitude>.Value = "XXX"
End Sub
This feature has to do with LINQ to XML, the basic object of which is the XElement
class we are declaring above. You can with this embed XML directly in your VB
code. Notice there are no line continuation characters after the root node opens,
this is because it is assumed that the declare will end when the root node is closed.
Also note the use of the <% and %> like ASP and ASP.NET use to embed code into
the XML. This simple example is simply outputting variables, but there is no
reason why this couldn’t just as easily have contained a For loop or some other more
complex code.
The final line before the end of the sub shows how you can also use the XML node syntax
as if they were an object model. Now this does not include Intellisense support
for the .<tag> names, but it is still pretty slick.
Lambda Expressions
I know that Visual Basic has a tradition of being a less terse, more verbose language,
but I personally feel that VB got the short end of the stick on Lambda Expression
syntax. A Lambda expression is supposed to be a terse syntax for anonymous delegates
which also happens to be able to be explored via code if you’re using Expression Trees
(which we will not discuss here). For instance, let’s assume that you wanted
to select items from an List(of Integer) where the value was greater than 50.
You could write code like this:
Function GreaterThan50(ByVal inputList As List(Of Integer)) As List(Of Integer)
Return inputList.Where(Function(i) i > 50)
End Function
This is much more verbose than the C# syntax for that Lambda Expression “i => i
> 50” but I will grant you it is more readable so hopefully people who prefer VB
will prefer the VB syntax.
Extension Methods
The best feature of the .NET Framework 3.5, hands down, is extension methods.
If anyone has sold you on LINQ as the best feature, laugh at them and remind them
that LINQ is entirely based on extension methods.
What are they? Simple : The ability to add a new method to an existing type which
is only visible to those who implement your extension library.
Restrictions? Some. The biggest is that you do not get access to anything you
wouldn’t have had access to already. So creating an extension method is not
going to allow you to get access to private or internal types for a class.
Benefits? Huge! As cool as being able to add new methods to classes is,
you’ll please note I said you could extend types! This means
you can create an Interface and then provide implementation for that
interface via extension methods. In fact, that’s how LINQ is done in great part.
The .NET Framework now contains extension methods such as “Where” and “First” and
“GroupBy”for the IEnumerable interface. As such any IEnumerable can use
these, without changing the interface at all.
Very important note to those who try to stay fluent in both languages, the VB syntax
for extension methods is very different than the C# syntax, you’ll
want to pay very close attention to this.
Here are two examples, one extends a class, the other an interface:
<Extension()> _
Function ToStringNullSafe(ByVal inObj AsObject) AsString
If inObj IsNothingThen
Return String.Empty
Else
Return inObj.ToString()
End If
End Function
<Extension()> _
Sub Dispose(ByVal inObj As IDisposable)
If inObj IsNot NothingThen
inObj.Dispose()
End If
End Sub
The first of these extension methods is something every UI developer has wished for,
a ToString function which does not blow up just because the variable is Nothing.
The second is an ingenious (if I say so myself) and beautiful hack of the IDisposable
interface. Most, but importantly not all, objects which implement IDisposable
implement a public Dispose method. Some however do not, even though they implement
it explicitly. This method will add a public Dispose method to any object which
implements IDisposable.
What if they already have one you ask? The actual method on an object always
wins out, and if there is a danger to extension methods, that is it. Should
the person who controls that type add a method which has the same name as your extension
method, your extension method becomes uncallable. A minimal risk for the incredible
power of being able to provide implementation to interfaces.