class ExtensionMethods2
{ static void Main() { int[] ints = { 10, 45, 15, 39, 21, 26 }; var result = ints.OrderBy(g => g); foreach (var i in result) { System.Console.Write(i + " "); } } } //Output: 10 15 21 26 39 45
namespace ExtensionMethods
{ public static class MyExtensions { public static int WordCount(this String str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } } }
Để dùng được PTMR WordCount bạn phải khai báo:
using ExtensionMethods;
Và nó có thể được gọi sử dụng cú pháp sau:
string s = "Hello Extension Methods";
int i = s.WordCount();
Bạn gọi extension method với cú pháp của một instance method. Tuy nhiên đoạn lệnh IL được tạo ra bởi trình dịch sẽ diễn dịch đoạn lệnh gọi của bạn thành một lời gọi đến phương thức tĩnh. Do vậy, khái niệm về đóng gói (encapsulation) không bị xâm phạm, và thực sự, các extension method cũng không thể truy cập đến các biến private bên trong kiểu dữ liệu chúng mở rộng.
Để có thêm thông tin, xin đọc How to: Implement and Call a Custom Extension Method (C# Programming Guide).
Nói chung, bạn có lẽ sẽ gọi đến các extension method nhiều hơn nhiều so với việc viết ra chúng. Bởi vì các extension method được gọi dùng cú pháp như cách gọi các instance method, nên không cần kiến thức gì đặc biệt để sử dụng chúng. Để cho phép các extension method cho một kiểu nào đó, chỉ cần thêm vào một khai báo using cho namespace trong các phương thức được định nghĩa. Ví dụ, để dùng các toán tử truy vấn chuẩn, thêm khai báo sau vào chương trình của bạn:
using System.Linq;
Binding các phương thức mở rộng lúc biên dịch
Bạn có thể dùng các extension method để mở rộng một class hay một interface, nhưng không thể override chúng, nếu bạn viết một phương thức mở rộng trùng tên và tham số với một phương thức có trong lớp được mở rộng, chúng sẽ không bao giờ được gọi. Khi biên dịch, các extension method có độ ưu tiên thấp hơn các phương thức có trong kiểu dữ liệu được mở rộng. Nói cách khác, nếu một kiểu có phương thức tên Process(int i), và bạn có một extension method được khai báo giống hệt, trình dịch của bạn sẽ luôn dùng phương thức có sẵn trong kiểu. Khi trình dịch gặp một lời gọi phương thức, chúng sẽ luôn tìm trong các phương thức của lớp trước, nếu không thấy, chúng mới bắt đầu tìm các phương thức mở rộng, và gắn lời gọi vào nếu thấy. Ví dụ sau biểu diễn việc trình dịch xác định extension method hay instance method để gắn lời gọi vào.
Ví dụ:
Ví dụ sau biểu diễn các quy tắc trình dịch C# sử dụng để xác định xem liệu một lời gọi sẽ gọi đến instance method hay extension method. Lớp Extensions được khai báo là static và chứa các extension method cho bất kỳ kiểu nào thừa kế IMyInterface.Phương thức mở rộng MethodB không bao giờ được gọi vì tên và các thuộc tính của nó trùng hoàn toàn với các phương đã được định nghĩa trong interface.
Khi trình dịch không thể tìm thấy phương thức thuộc lớp nào khớp, nó sẽ tìm đến các phương thức mở rộng để sử dụng.
// Define an interface named IMyInterface. namespace DefineIMyInterface { using System; public interface IMyInterface { // Any class that implements IMyInterface must define a method // that matches the following signature. void MethodB(); } } // Define extension methods for IMyInterface. namespace Extensions { using System; using DefineIMyInterface; // The following extension methods can be accessed by instances of any // class that implements IMyInterface. public static class Extension { public static void MethodA(this IMyInterface myInterface, int i) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, int i)"); } public static void MethodA(this IMyInterface myInterface, string s) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, string s)"); } // This method is never called in ExtensionMethodsDemo1, because each // of the three classes A, B, and C implements a method named MethodB // that has a matching signature. public static void MethodB(this IMyInterface myInterface) { Console.WriteLine ("Extension.MethodB(this IMyInterface myInterface)"); } } } // Define three classes that implement IMyInterface, and then use them to test // the extension methods. namespace ExtensionMethodsDemo1 { using System; using Extensions; using DefineIMyInterface; class A : IMyInterface { public void MethodB() { Console.WriteLine("A.MethodB()"); } } class B : IMyInterface { public void MethodB() { Console.WriteLine("B.MethodB()"); } public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); } } class C : IMyInterface { public void MethodB() { Console.WriteLine("C.MethodB()"); } public void MethodA(object obj) { Console.WriteLine("C.MethodA(object obj)"); } } class ExtMethodDemo { static void Main(string[] args) { // Declare an instance of class A, class B, and class C. A a = new A(); B b = new B(); C c = new C(); // For a, b, and c, call the following methods: // -- MethodA with an int argument // -- MethodA with a string argument // -- MethodB with no argument. // A contains no MethodA, so each call to MethodA resolves to // the extension method that has a matching signature. a.MethodA(1); // Extension.MethodA(object, int) a.MethodA("hello"); // Extension.MethodA(object, string) // A has a method that matches the signature of the following call // to MethodB. a.MethodB(); // A.MethodB() // B has methods that match the signatures of the following // nethod calls. b.MethodA(1); // B.MethodA(int) b.MethodB(); // B.MethodB() // B has no matching method for the following call, but // class Extension does. b.MethodA("hello"); // Extension.MethodA(object, string) // C contains an instance method that matches each of the following // method calls. c.MethodA(1); // C.MethodA(object) c.MethodA("hello"); // C.MethodA(object) c.MethodB(); // C.MethodB() } } } /* Output: Extension.MethodA(this IMyInterface myInterface, int i) Extension.MethodA(this IMyInterface myInterface, string s) A.MethodB() B.MethodA(int i) B.MethodB() Extension.MethodA(this IMyInterface myInterface, string s) C.MethodA(object obj) C.MethodA(object obj) C.MethodB() */
Hướng dẫn chung
- Một phương thức mở rộng sẽ không bao giờ được gọi nếu nó được khai báo tên và tham số trùng hoàn toàn với một phương thức có trong kiểu dữ liệu đó.
- Các phương thức mở rộng sẽ được tìm kiếm ở cấp độ namespace. Ví dụ, nếu bạn có nhiều lớp static chứa các phương thức mở rộng bên trong namespace có tên Extensions, tất cả chúng sẽ trở nên dùng được ngay khi bạn sử dụng câu lệnh using Extensions;
Dịch từ http://msdn.microsoft.com/en-us/library/bb383977.aspx
Bài viết hữu ích quá, em cảm ơn anh rất nhiều ạ