WEBINAR:
On-Demand
Building the Right Environment to Support AI, Machine Learning and Deep Learning
Extension Methods
How many times have you wanted to add functionality to a .NET Framework class to provide helper functions for your project? For example, suppose you wanted to create a derived DateTime method class that would return a date formatted in a specific way. If you've ever tried this, you've probably found that, in many cases, the class you want to extend is marked as
Sealed, meaning you cannot derive a new MyDateTime class to add your methods to.
VB9 solves this problem with my favorite new feature, Extension Methods. Using extension methods, you can extend a type by adding methods that
appear to be instance methods.
Extension methods are just like normal methods but have two important differences. First, you apply the System.Runtime.CompilerServices.Extension attribute to the method to instruct the IDE to treat the method as an extension method. Second, the first parameter of the method is always the type you want to extend. Here's an example. Suppose you want to code an extension to the DateTime class to add a method named
ToLogFileString, which returns a string formatted in a specific way for your log file:
<Extension()> _
Function ToLogFileString(ByVal dte As DateTime) As String
Return dte.ToShortDateString & " " & dte.ToShortTimeString
End Function
Note the
attribute on the method. Of course, you must ensure that namespace where the extension method is defined is visible to the code where you call the extension method; in some cases you will need to import the appropriate namespace. Having done that, you can then call the extension method just as though it were an instance method:
strLogFile += Now.ToLogFileString()
The trick behind extension methods is that VB9 helps you out a bit with the syntax of the call. As
Figure 10) shows, it even includes the extension method in IntelliSense for the DateTime type!
 | |
Figure 10. Intellisense Extension Methods: Extension methods even appear in IntelliSense lists, making them even easier to use. |
Behind the scenes, VB9 translates your call (which looks like a call to an instance method) to a shared method call, passing the base object as the first parameter of the call to the extension method. In the example above, the DateTime object returned from the call to
Now is provided as the first parameter to the
ToLogFileString method.
Because the object gets passed as the first parameter, extension methods can do things that normal instance methods (methods defined in the object's class) can't do. For example, I have always disliked the way the String class's
IsNullOrEmpty method is called. Since you can't call methods on an object that is
NULL, you must call
IsNullOrEmpty as a shared method:
String.IsNullOrEmpty(strTemp)
However, if you define an extension method to handle the same function (renamed to
IsEmptyOrNull—you can't shadow an existing method on the extended class), the extension method can detect
NULL values passed in as the parameter. You can use this to detect
NULL strings:
<Extension()> _
Function IsEmptyOrNull(ByVal str As String) As Boolean
Return String.IsNullOrEmpty(str)
End Function
Now you can call the
IsEmptyOrNull extension method even on String variables that are
NULL:
blnValidString = str.IsEmptyOrNull()
Be careful, though, this capability has a side effect; it requires you to verify that the first parameter is not
NULL before using it. Be sure your extension methods verify that the first parameter is valid before attempting to use it.
Extension methods even work on derived types. You can define an extension method named
IsSomething that returns a Boolean value indicating whether the variable is set to an instance of an object:
<Extension()> _
Function IsSomething(ByVal obj As Object) As Boolean
Return obj IsNot Nothing
End Function
By defining the first parameter as Object, you can now use the
IsSomething extension method against any class. Here are several examples:
Dim bln as Boolean
Dim strName As String
bln = strName.IsSomething()
Dim frmMain As System.Windows.Forms.Form
bln = frmMain.IsSomething()
Dim objThread As System.Threading.Thread
bln = objThread.IsSomething()
There are several more important points to make about extension methods:
- Add any additional parameters you need on the extension method after the first parameter, which must be the object itself.
- You can overload extension methods just like conventional methods, using the same rules governing method overloading.
- You may define extension methods in any assembly. They can be defined in the same project or in a separately compiled assembly—you just have to ensure that the method's namespace is visible by the code that will use the extension method.
These three points are demonstrated in the
VB9Extensions.vb file in the sample
VB9_Tools project. You'll find two different versions of the
ToLogFileString method implemented; one has only the special "type" parameter and the other includes a second parameter that the caller must provide. The code below shows calls to both methods:
str = Now.ToLogFileString()
str = Now.ToLogFileString(strModuleName)
Using Extension Methods in .NET 2.0
By default, you cannot create extension methods when you target the 2.0 framework. Recall that you use the
Extension attribute to declare extension methods. This attribute is new to the .NET Framework version 3.5, and doesn't exist in the 2.0 framework. However, it is possible to "hack" your .NET 2.0 projects slightly to support extension methods.
To support extension methods in .NET 2.0 projects, you must define your own Extension attribute. I recommend creating a separate assembly to define the extension attribute. The sample code contains a project named
ExtensionsIn20 containing a code file that defines an
ExtensionAttribute class. The class file is very small; here's the complete code.
Namespace System.Runtime.CompilerServices
Public Class ExtensionAttribute
Inherits Attribute
End Class
End Namespace
Author's Note: It is important to make sure the Root Namespace property in the project properties is empty. Otherwise, the namespace declaration shown above will be appended to the project's root namespace value. |
Compile the
ExtensionsIn20 project, and add a reference to it from a Visual Basic project targeting the 2.0 framework where you want to define extension methods. In the module where you define your extension methods, import the System.Runtime.CompilerServices namespace, and decorate your extension methods exactly as you would when using the .NET 3.5 version of the attribute. When it comes time to upgrade your project to .NET 3.5, simply remove the reference to the
ExtensionsIn20 assembly, and your code will instead reference the Extension attribute defined in .NET 3.5's System.Runtime.CompilerServices namespace.
Overall, Visual Basic 9 is a compelling upgrade, especially because it's combined with some of the fantastic features in Visual Studio 2008. As discussed, there are some features you should be careful about using, and some that you should avoid completely. The advantages aren't limited to the newest framework version either. Even if your development group has not yet decided to upgrade to the .NET Framework 3.5, the new multi-targeting support means you can easily build .NET 2.0 projects from Visual Studio 2008.