Extension methods are static methods that can be invoked using the instance method syntax. In effect, extension methods make it possible for us to extend existing types and constructed types with additional methods.
For example, we can define an extension method as follows:
public static class MyExtensions { public static bool IsCandy(this Product p) { if (p.ProductName.IndexOf("candy") >= 0) return true; else return false; } }
In this example, the static method IsCandy
takes a this
parameter of Product
type, and searches for the word candy
inside the product name. If it finds a match, it assumes this is a candy
product and returns true
. Otherwise, it returns false
, meaning this is not a candy
product.
To simplify the example, we put this class inside the same namespace as our main test application, TestNewFeaturesApp
. Now, in the program, we can call this extension method like this:
if (product.IsCandy()) Console.WriteLine("yes, it is a candy"); else Console.WriteLine("no, it is not a candy");
It looks as if IsCandy
is a real instance method of the Product
class. Actually, it is a real method of the Product
class, but it is not defined inside the Product
class. Instead, it is defined in another static class.
Not only does it look like a real instance method, but this new extension method actually pops up when a dot is typed following the product
variable. The following image shows the intellisense of the product
variable within Visual Studio 2008.
Under the hood in Visual Studio 2008, when a method call on an instance is being compiled, the compiler first checks to see if there is an instance method in the class for this method. If there is no matching instance method, it looks for an imported static class, or any static class within the same namespace. It also searches for an extension method with the first parameter that is the same as the instance type (or is a super type of the instance type). If it finds a match, the compiler will call that extension method. This means that instance methods take precedence over extension methods, and extension methods that are imported in inner namespace declarations take precedence over extension methods that are imported in outer namespaces.
In our example, when product.IsCandy()
is being compiled, the compiler first checks the Product
class and doesn't find a method named IsCandy
. It then searches the static class, MyExtensions
, and finds an extension method with the name IsCandy
, and with a first parameter of the type, Product
.
At compile time, the compiler actually changes product.IsCandy()
to this call:
MyExtensions.IsCandy(product)
However, in the source code, this method is the same as another method defined inside the Product
class, and that is why it is called an extension method.
Surprisingly, extension methods can be defined for sealed classes. In our example, you can change the Product
class to be sealed, and it still runs without any problem. This gives us great flexibility to extend system types, because many of the system types are sealed.
On the other hand, extension methods are less discoverable, and are harder to maintain, so they should be used with great caution. If your requirements can be achieved with an instance method, one should never define an extension method to do the same work.
Not surprisingly, this new feature is again a Visual Studio 2008 compiler feature, and compiled assembly is a valid .NET 2.0 assembly.
Extension methods are the base of LINQ. We will discuss the various extension methods defined by .NET 3. in the namespace System.Linq
, later.
Now, the Program.cs
file should like this:
using System; LINQextension methodsusing System.Collections.Generic; using System.Linq; using System.Text; namespace TestNewFeaturesApp { class Program { static void Main(string[] args) { // valid var statements var x = "1"; var n = 0; string s = "string"; var s2 = s; s2 = null; string s3 = null; var s4 = s3; /* string x = "1"; int n = 0; string s2 = s; */ // invalid var statements /* var v; var nu = null; var v2 = "12"; v2 = 3; */ //object initializer /* Product p = new product(1, "first candy", 100.0); Product p = new Product(); p.ProductID = 1; p.ProductName = "first candy"; p.UnitPrice=(decimal)100.0; */ Product product = new Product { ProductID = 1, ProductName = "first candy", UnitPrice = (decimal)100.0 }; var arr = new[] { 1, 10, 20, 30 }; // collection initializer List<Product> products = new List<Product> { new Product { ProductID = 1, ProductName = "first candy", UnitPrice = (decimal)10.0 }, new Product { ProductID = 2, ProductName = "second candy", UnitPrice = (decimal)35.0 }, new Product { ProductID = 3, ProductName = "first vegetable", UnitPrice = (decimal)6.0 }, new Product { ProductID = 4, ProductName = "second vegetable", UnitPrice = (decimal)15.0 }, new Product { ProductID = 5, ProductName = "third product", UnitPrice = (decimal)55.0 } }; // anonymous types LINQextension methodsvar a = new { Name = "name1", Address = "address1" }; var b = new { Name = "name2", Address = "address2" }; b = a; /* class __Anonymous1 { private string name; private string address; public string Name { get{ return name; } set { name=value } } public string Address { get{ return address; } set{ address=value; } } } */ // extension methods if (product.IsCandy()) //if(MyExtensions.IsCandy(product)) Console.WriteLine("yes, it is a candy"); else Console.WriteLine("no, it is not a candy"); } } public sealed class Product { public int ProductID { get; set; } public string ProductName { get; set; } public decimal UnitPrice { get; set; } } public static class MyExtensions { public static bool IsCandy(this Product p) { if (p.ProductName.IndexOf("candy") >= 0) return true; else return false; } } }
In this example, we have
var
type variables Product
IsCandy
with a this
parameter of the type Product
, to it making this method an extension method candy
product, and printed out a message according to its name