12.4. Extension Methods

A design question that often arises is whether to extend a given interface to include a specific method. Extending any widely used interface, for example IEnumerable, will not only break contracts where the interface has been implemented but will also force any future class that implements the interface to include the new method. Although there are scenarios where this is warranted, there are also numerous cases in which you simply want to create a method to manipulate an object that matches a particular interface.

One possible example involves a Count method that simply iterates through an IEnumerable and returns the number of elements. Adding this method to the IEnumerable interface would actually increase the amount of redundant code, as each class implementing the interface would have to implement the Count method. A solution to this problem is to create a helper class that has a static Count method that accepts an IEnumerable parameter.

Public Shared Function Count(ByVal items As IEnumerable) As Integer
    Dim i As Integer = 0
    For Each x In items
        i += 1
    Next
    Return i
End Function

Dim cnt = Count(people)

While this solution is adequate in most scenarios, it can lead to some terse-looking code when you use multiple static methods to manipulate a given object. Extension methods promote readability by enabling you to declare static methods that appear to modify the public type information for a class or interface. For example, in the case of the IEnumerable interface there are built-in extension methods that enable you to call people.Count in order to access the number of items in people, a variable declared as an IEnumerable. As you can see from the IntelliSense in Figure 12-5, the Count method appears as if it were an instance method, although it can be distinguished by the extension method icon to the left of the method name and the <Extension> prefix in the tooltip information.

Figure 12.5. Figure 12-5

All extension methods are static, must be declared in a static class (or a module in the case of VB.NET), and must be marked with the Extension attribute. The Extension attribute informs the compiler that the method should be available for all objects that match the type of the first argument. In the following example, the extension method would be available in the IntelliSense list for Person objects, or any class that derives from Person. Note that the C# snippet doesn't explicitly declare the Extension attribute; instead, it uses the this keyword to indicate that it is an extension method.

VB.NET

Public Module PersonHelper
    <System.Runtime.CompilerServices.Extension()> _
    Public Function AgeMetric(ByVal p As Person) As Double
        Return p.Age / 2 + 7
    End Function
End Module

C#

public static class PersonHelper
{
    public static double AgeMetric(this Person p)
    {
        return p.Age/2 +7;
    }
}

In order to make use of extension methods, the static class (or module in VB.NET) has to be brought into scope via a using statement (or imports in VB.NET). This is true even if the extension method is in the same code file in which it is being used.

VB.NET

imports PersonHelper

C#

using PersonHelper;

When an extension method is invoked it is done almost the same way as for any conventional static method, the difference being that instead of all parameters being explicitly passed in, the first argument is inferred by the compiler from the calling context. In the preceding example, the collection people would be passed into the Count method. This, of course, means that there is one less argument to be specified when an extension method is being called.

Although extension methods are called in a similar way to instance methods, they are limited to accessing the public methods and properties of the arguments. Because of this it is common practice for extension methods to return new objects, rather than modifying the original argument. Following this practice means that extension methods can easily be chained. For example, the following code snippet takes persons 10 to 15, in reverse order from the people collection.

Dim somePeople = people.Skip(10).Take(5).Reverse()

Each of the three extension methods — Skip, Take, and Reverse — accepts an IEnumerable as its first (hidden) argument and returns a new IEnumerable. The returned IEnumerable is then passed into the subsequent extension method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.133.233