When you use a class in .NET programming, you generally think about using its properties, methods, and events. Almost always, the methods of a class provide the functionality you need. Sometimes, you find yourself working with a class that doesn't have exactly the right method. If you have the source code for the class, you can usually add a new method (C#) or subroutine or function (VB).
What if you don't have the source code, though? For example, what if you discover that you would like to add a method to a class in the .NET Framework? The usual solution is to create a child class that inherits from the base class, adding the new method to the child class. Although this is an acceptable solution, there is a new technique introduced with Visual Basic 2008 and C# 3.0 that may be simpler to use. That technique is called extension methods. As you'll see in the examples I've provided, it is relatively easy to understand and use extension methods.
For the example, I created Visual Basic C# projects that use a simple Console application to prompt for an integer value and a string field. The integer value is checked to see if it falls within a range of values. The string field is checked to see if it is within a list of valid acceptable values. Those checks are roughly equivalent to the DDS keywords RANGE and VALUES.
Figure 1 shows the output of the example application at runtime. You'll see the same output for all the example code for this article; the only difference is the identifying text at the top of the Console window. For example, Figure 1 is the output of the NonExtensionExample C# class. Figure 2 shows the code for the Visual Basic NonExtensionExample. Figure 3 is the code for the C# NonExtensionExample.
In both of these examples, there are two classes (in Visual Basic, the classes are static classes, identified as modules). The NonExtensionExample module (Figure 2) or class (Figure 3) is a series of Console prompts and output statements. There is nothing tricky about the code; the code also does not include any error handling. If you enter a non-integer value when prompted, the example will blow up.
You can see in Figure 2 and Figure 3 that the CheckExtensions class is used for the CheckRange and CheckValues method calls. The CheckExtensions class is also included in Figure 2 and Figure 3, following the NonExtensionExample class. The CheckRange and CheckValues methods are entirely conventional. You simply pass in the value to check and the range or list of values to check against. If the value you are checking is valid, a "true" value is returned; otherwise a "false" value is returned. Back in the NonExtensionExample class, the return value is used to format the output statements, using the Visual Basic Immediate If (IIf) function in Figure 2 and the C# ternary conditional operator in Figure 3.
The code shown in the CheckExtensions class is the way we are accustomed to writing methods. Simply add methods to a class, accept parameters, and return a value.
Now look at the ExtensionExample code in Figure 4 (VB) and Figure 5 (C#). By design, most of the code is exactly the same. I will now point out an example of where the extension methods are used and then describe how you code extension methods.
In the ExtensionExample class, in the Main method, locate the Console.WriteLine statements that display the values of the fields that you enter. You now see code that looks like this:
i.CheckRange(1, 10)
and
state.CheckValues("CA", "NY", "TX", "FL", "NC")
Keep in mind that the variable "i" is defined as an Integer (System.Integer) data type, and the variable "state" is a System.String. If you go to the MSDN documentation for those data types in the .NET Framework, you'll find that there is no method named "CheckRange" defined for System.Integer and no method named "CheckValues" defined for System.String. Yet here is working code that is clearly invoking methods of those names on those data types. In fact, this is the meaning of the phrase "extension method": you can extend a class with methods that you create. Although the technique is similar to what you can do by inheriting from a base class, you can create extension methods for types that you can't inherit from. For example, the statement
i.CheckRange(1, 10)
is an extension method of the System.Integer type. You cannot inherit from System.Integer to create a new child class, so using inheritance you have no opportunity to add new methods to the System.Integer type.
Extension methods must be defined as static methods. In Figure 4 (VB) this is accomplished by defining the CheckRange and CheckValues functions inside a module, which by definition is a static class. In Figure 5 (C#), the static keyword is used for both the class and for the methods.
When you create an extension method, you must be careful to assign a method name that is not already in use by the type and that is unlikely to ever be assigned to the type. If you create an extension method name that is already used as method name in the type or one of its superclasses, or that in the future is defined as a method of the type, the method that is created directly as part of the type makes your extension method invisible.
Figure 4 shows the extension methods defined in the Visual Basic CheckExtensions module. First, note the Imports statement for System.Runtime.CompilerServices at the top of the source file. That Imports statement is required so that you can add the
In Visual Basic, you can define an extension method using either a Sub or a Function. The only difference is that a Sub does not return a value, whereas a Function does.
No matter what you use, the first parameter is required and it must be the data type that you want to apply the extension method to. For example, the first parameter of the CheckRange function is defined as Integer. That means that the CheckRange function will be treated as an extension method of the System.Integer type. In the CheckValues function, the first parameter is defined as a String, so CheckValues is an extension method of the System.String type. Following the required first parameter you simply define the additional parameters that are needed for the method. The code inside the extension methods is routine code; all the work of defining an extension method occurs with the
When you invoke an extension method, you pass one fewer parameters than you have defined:
i.CheckRange(1, 10)
Even though three parameters are defined for the CheckRange function, the call is made with only two parameters. As soon as you define an extension method, you'll see it in Visual Studio IntelliSense (prompting). Figure 6 shows an example of the IntelliSense prompting on the integer variable "i." The icon within the IntelliSense is different from the usual icon for a method, and the pop-up help clearly labels the method with the (extension) modifier.
Figure 5 shows the extension methods for C#. Compared with the Visual Basic version, there is almost nothing you need to do to make a method an extension method. The only required coding is in the first parameter to each method, where you add the keyword "this" before the type of the parameter. For example, the first parameter for the CheckRange method is defined as
this int number
The keyword "this" in the parameter definition is what indicates to the compiler that this is an extension method. You can only use the "this" keyword for the first parameter, and you are required to have at least one parameter, again used to indicate the type that the extension method is used with.
As you can see, coding extension methods is not difficult. When you use an extension method, your code usually looks simpler:
CheckExtensions.CheckRange(i, 1, 10)
compard with
i.CheckRange(1, 10)
In addition to needing to be careful to choose extension method names that don't currently conflict with existing method names and are not likely to be added to the type that you are extending, you need to be aware of how your use of extension methods will stand up in the future. Another programmer working with the code might spend quite a bit of time trying to figure out where the CheckRange method is for the Integer type. As shown in these short examples, you may want to consider putting extension methods into separate classes that are used for clearly defined purposes. For example, if I wanted to create a series of edit checks to mimic the features provided in DDS, I would group the extension methods into the same class.
Microsoft documentation and other references indicate that extension methods were added to the .NET languages primarily to support LINQ (Language Integrated Query). However, you're free to use the technique for your own code, if you choose to extend yourself.
Craig Pelkie (craig@web400.com) has worked as a programmer with IBM midrange computers for many years. He has also written and lectured extensively on AS/400 and System i technologies, including client/server programming, Client Access, Java, WebSphere, .NET applications for the System i, and web development.