Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Chapter 48: Extension Methods

Parameter

Details

 

The first parameter of an extension method should always be preceded by the

 

keyword, followed

this

this

by the identifier with which to refer to the "current" instance of the object you are extending

 

Section 48.1: Extension methods - overview

Extension methods were introduced in C# 3.0. Extension methods extend and add behavior to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. They are especially helpful when you cannot modify the source of a type you are looking to enhance. Extension methods may be created for system types, types defined by third parties, and types that you have defined yourself. The extension method can be invoked as though it were a member method of the original type. This allows for Method Chaining used to implement a Fluent Interface.

An extension method is created by adding a static method to a static class which is distinct from the original type being extended. The static class holding the extension method is often created for the sole purpose of holding extension methods.

Extension methods take a special first parameter that designates the original type being extended. This first parameter is decorated with the keyword this (which constitutes a special and distinct use of this in C#—it should be understood as di erent from the use of this which allows referring to members of the current object instance).

In the following example, the original type being extended is the class string. String has been extended by a method Shorten(), which provides the additional functionality of shortening. The static class StringExtensions has been created to hold the extension method. The extension method Shorten() shows that it is an extension of string via the specially marked first parameter. To show that the Shorten() method extends string, the first parameter is marked with this. Therefore, the full signature of the first parameter is this string text, where string is the original type being extended and text is the chosen parameter name.

static class StringExtensions

{

public static string Shorten(this string text, int length)

{

return text.Substring(0, length);

}

}

class Program

{

static void Main()

{

//This calls method String.ToUpper() var myString = "Hello World!".ToUpper();

//This calls the extension method StringExtensions.Shorten() var newString = myString.Shorten(5);

//It is worth noting that the above call is purely syntactic sugar

//and the assignment below is functionally equivalent

var newString2 = StringExtensions.Shorten(myString, 5);

}

}

Live Demo on .NET Fiddle

GoalKicker.com – C# Notes for Professionals

194

The object passed as the first argument of an extension method (which is accompanied by the this keyword) is the instance the extension method is called upon.

For example, when this code is executed:

"some string".Shorten(5);

The values of the arguments are as below:

text: "some string" length: 5

Note that extension methods are only usable if they are in the same namespace as their definition, if the namespace is imported explicitly by the code using the extension method, or if the extension class is namespace-less. The .NET framework guidelines recommend putting extension classes in their own namespace. However, this may lead to discovery issues.

This results in no conflicts between the extension methods and the libraries being used, unless namespaces which might conflict are explicitly pulled in. For example LINQ Extensions:

using System.Linq; // Allows use of extension methods from the System.Linq namespace

class Program

{

static void Main()

{

var ints = new int[] {1, 2, 3, 4};

// Call Where() extension method from the System.Linq namespace var even = ints.Where(x => x % 2 == 0);

}

}

Live Demo on .NET Fiddle

Since C# 6.0, it is also possible to put a using static directive to the class containing the extension methods. For example, using static System.Linq.Enumerable;. This makes extension methods from that particular class available without bringing other types from the same namespace into scope.

When a class method with the same signature is available, the compiler prioritizes it over the extension method call. For example:

class Test

{

public void Hello()

{

Console.WriteLine("From Test");

}

}

static class TestExtensions

{

public static void Hello(this Test test)

{

Console.WriteLine("From extension method");

}

}

GoalKicker.com – C# Notes for Professionals

195

class Program

{

static void Main()

{

Test t = new Test();

t.Hello(); // Prints "From Test"

}

}

Live demo on .NET Fiddle

Note that if there are two extension functions with the same signature, and one of them is in the same namespace, then that one will be prioritized. On the other hand, if both of them are accessed by using, then a compile time error will ensue with the message:

The call is ambiguous between the following methods or properties

Note that the syntactic convenience of calling an extension method via originalTypeInstance.ExtensionMethod() is an optional convenience. The method can also be called in the traditional manner, so that the special first parameter is used as a parameter to the method.

I.e., both of the following work:

//Calling as though method belongs to string--it seamlessly extends string

String s = "Hello World"; s.Shorten(5);

//Calling as a traditional static method with two parameters

StringExtensions.Shorten(s, 5);

Section 48.2: Null checking

Extension methods are static methods which behave like instance methods. However, unlike what happens when calling an instance method on a null reference, when an extension method is called with a null reference, it does not throw a NullReferenceException. This can be quite useful in some scenarios.

For example, consider the following static class:

public static class StringExtensions

{

public static string EmptyIfNull(this string text)

{

return text ?? String.Empty;

}

public static string NullIfEmpty(this string text)

{

return String.Empty == text ? null : text;

}

}

string nullString = null;

string emptyString = nullString.EmptyIfNull();// will return ""

string anotherNullString = emptyString.NullIfEmpty(); // will return null

Live Demo on .NET Fiddle

 

GoalKicker.com – C# Notes for Professionals

196

Section 48.3: Explicitly using an extension method

Extension methods can also be used like ordinary static class methods. This way of calling an extension method is more verbose, but is necessary in some cases.

static class StringExtensions

{

public static string Shorten(this string text, int length)

{

return text.Substring(0, length);

}

}

Usage:

var newString = StringExtensions.Shorten("Hello World", 5);

When to call extension methods as static methods

There are still scenarios where you would need to use an extension method as a static method:

Resolving conflict with a member method. This can happen if a new version of a library introduces a new member method with the same signature. In this case, the member method will be preferred by the compiler.

Resolving conflicts with another extension method with the same signature. This can happen if two libraries include similar extension methods and namespaces of both classes with extension methods are used in the same file.

Passing extension method as a method group into delegate parameter. Doing your own binding through Reflection.

Using the extension method in the Immediate window in Visual Studio.

Using static

If a using static directive is used to bring static members of a static class into global scope, extension methods are skipped. Example:

using static OurNamespace.StringExtensions; // refers to class in previous example

//OK: extension method syntax still works.

"Hello World".Shorten(5);

//OK: static method syntax still works.

OurNamespace.StringExtensions.Shorten("Hello World", 5);

// Compile time error: extension methods can't be called as static without specifying class.

Shorten("Hello World", 5);

If you remove the this modifier from the first argument of the Shorten method, the last line will compile.

Section 48.4: Generic Extension Methods

Just like other methods, extension methods can use generics. For example:

static class Extensions

{

public static bool HasMoreThanThreeElements<T>(this IEnumerable<T> enumerable)

{

return enumerable.Take(4).Count() > 3;

}

GoalKicker.com – C# Notes for Professionals

197

}

and calling it would be like:

IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};

var hasMoreThanThreeElements = numbers.HasMoreThanThreeElements();

View Demo

Likewise for multiple Type Arguments:

public static TU GenericExt<T, TU>(this T obj)

{

TU ret = default(TU);

// do some stuff with obj return ret;

}

Calling it would be like:

IEnumerable<int> numbers = new List<int> {1,2,3,4,5,6};

var result = numbers.GenericExt<IEnumerable<int>,String>();

View Demo

You can also create extension methods for partially bound types in multi generic types:

class MyType<T1, T2>

{

}

static class Extensions

{

public static void Example<T>(this MyType<int, T> test)

{

}

}

Calling it would be like:

MyType<int, string> t = new MyType<int, string>(); t.Example();

View Demo

You can also specify type constraints with where :

public static bool IsDefault<T>(this T obj) where T : struct, IEquatable<T>

{

return EqualityComparer<T>.Default.Equals(obj, default(T));

}

Calling code:

int number = 5;

var IsDefault = number.IsDefault();

GoalKicker.com – C# Notes for Professionals

198