Cấp bậc tác giả:

TRAINING

C# 3.0 – Tìm hiểu về Extension Method

Được viết bởi QuangIT ngày 07/05/2013 lúc 11:29 AM
Một trong những tính năng mà tôi thấy đơn giản và đáng giá nhất trong phiên bản C# 3.0 là Extension Method. Extension method giúp bạn tạo thêm các phương thức cho một lớp mà không cần thừa kế lại lớp đó.
  • 0
  • 30521

C# 3.0 – Tìm hiểu về Extension Method

Một trong những tính năng mà tôi thấy đơn giản và đáng giá nhất trong phiên bản C# 3.0 là Extension Method. Extension method giúp bạn tạo thêm các phương thức cho một lớp mà không cần thừa kế lại lớp đó. Với tính năng này, thậm chí khi đối tượng của bạn là null, bạn vẫn có thể thực hiện các phương thức extension của đối tượng đó một cách an toàn.

Điều đầu tiên tôi muốn đề cập trong bài viết này là có một trang web được tạo ra để cung cấp những extension method cho các ngôn ngữ C# 3.0, F# và Visual Basic 2008. Bất kì ai cũng có thể tham gia và giới thiệu extension method của mình lên trang web này. Đây quả là thư viện mở hữu ích về extension method mà bạn nên ghé qua:


Nhưng trước khi làm điều đó, bạn cần biết cách hiện thực cũng như sử dụng extension method như thế nào.

Các quy tắc khi định nghĩa và sử dụng extension method

-          Lớp chứa extension method phải là static
-          Extension method cũng phải là một phương  thức static
-          Tham số đầu tiên của extension method xác định kiểu của đối tượng được sử dụng (extension method sẽ thêm vào lớp của đối tượng đó) với từ khóa this. Tham số này sẽ được bỏ qua khi bạn gọi extension method.

Một điểm cơ bản cần chú ý là khi sử dụng extension method: bởi vì chúng không thực sự nằm trong các lớp mà chúng được thêm vào, nên bạn cần chắc chắn rằng các extension method được khai báo để có thể truy xuất được tại nơi mà bạn cần sử dụng như modifier, namespace. Giống như khi bạn sử dụng Linq, bạn cần phải thêm namespace System.Linq.

Ví dụ

Một công việc mà bạn hay thực hiện là parse một chuỗi thành kiểu số. Mỗi lần như thế bạn phải gọi phương thức static của Int32 hoặc float,… Bây giờ tôi muốn tạo một phương thức instance để tất cả các đối tượng có thể chuyển đổi nhanh chóng sang kiểu Int32. Cách thực hiện rất đơn giản là bạn tạo thêm một lớp với phương thức ToInt32() như sau:

public static class Y2Extensions
{
    public static int ToInt32(this object obj)
    {
        return Int32.Parse(obj.ToString());
    }
}
Tham số duy nhất của phương thức trên là một kiểu object, tức là tôi muốn phương thức ToInt32() này được gắn vào tất cả các đối tượng thừa kế từ object (dĩ nhiên là nó sẽ ảnh hướng đến mọi kiểu đối tượng). Phương thức này làm việc tốt với những kiểu dữ liệu cơ bản, tuy nhiên bạn cần cẩn thận với phương thức ToString() của các đối tượng khi chúng trả về những giá trị rất khác nhau.

Sau khi định nghĩa, bạn có thể sử dụng phương thức ToInt32() này như một phương thức instance của lớp object. Các extension method có thể được nhận ra khi bạn đọc các tooltip mô tả phương thức bởi chữ “(extension)“ phía trước (Visual Studio):

namespace TestExtensions
{
    class Program {
 
        public static void Main(){
 
            string s="100";
            int i=s.ToInt32();
            i++;
            Console.WriteLine(i);
            Console.ReadKey();
        }
    }
 
    public static class Y2Extensions
    {
        public static int ToInt32(this object obj)
        {
            return Int32.Parse(obj.ToString());
        }
    }
}

Output:
101

Extension method vs instance method

Giả sử bạn tạo hai extension method cho có hiệu lực trong cùng một lớp và phạm vi. Compiler sẽ không biết được bạn muốn gọi method nào và sẽ thông báo lỗi. Ta có ví dụ sau:

namespace TestExtensions
{
    class Program {
 
        public static void Main(){
 
            string name =new Program().GetName();
            Console.WriteLine(name);
            Console.ReadKey();
        }
    }
 
    static class Y2Extension1
    {
        public static string GetName(this Program obj)
        {
            return "Extension1";
        }
    }
    static class Y2Extension2
    {
        public static string GetName(this Program obj)
        {
            return "Extension2";
        }
    }
}
Và ta nhận được lỗi:

The call is ambiguous between the following methods or properties:
‘TestExtensions.Y2Extension1.GetName(TestExtensions.Program)’ and ‘TestExtensions.Y2Extension2.GetName(TestExtensions.Program)’

Tuy nhiên nếu như có một phương thức instance trong cùng lớp đó (Program), lỗi này sẽ không xuất hiện bởi vì compiler sẽ ưu tiên gọi phương thức instance và không xét đến hai extension method này. Ta nhận định rằng instance method có độ ưu tiên cao hơn extension method.

namespace TestExtensions
{
    class Program {
 
        public static void Main(){
 
            string name =new Program().GetName();
            Console.WriteLine(name);
            Console.ReadKey();
        }
        public string GetName()
        {
            return "Program";
        }
    }
 
    static class Y2Extension1
    {
        public static string GetName(this Program obj)
        {
            return "Extension1";
        }
    }
    static class Y2Extension2
    {
        public static string GetName(this Program obj)
        {
            return "Extension2";
        }
    }
}
Output:

Program

Từ khóa “this” trong Extension method

Khi phân tích chương trình có sử dụng extension method ra mã IL. Tôi nhận thấy rằng các extension method này sử dụng một attribute là System.Runtime.CompilerServices.ExtensionAttribute. ExtensionAttribute này có nhiệm vụ chỉ ra một phương thức là một extension method hoặc assembly, class có chứa extension method. Và sự xuất hiện của attribute này là do  từ khóa “this” bạn đặt trước tham số đầu của extension method.

Mã IL của lớp Y2Extension1 như sau (phân tích bằng ILSpy):

.class private auto ansi abstract sealed beforefieldinit Y2Extension1 extends object
{
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 0 )
// Methods
.method public static hidebysig
string GetName (class TestExtensions.Program obj) cil managed
{
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00)
// Method begins at RVA 0×2098
// Code size 11 (0xb)
.maxstack 1
.locals init ([0] string)

IL_0000: nop
IL_0001: ldstr ”Extension1″
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
} // End of method Y2Extension1.GetName

} // End of class TestExtensions.Y2Extension1

Tuy nhiên bạn không thể sử dụng attribute này thay cho từ khóa “this”. Nếu thử làm điều này, bạn sẽ nhận được lỗi biên dịch:

Do not use ‘System.Runtime.CompilerServices.ExtensionAttribute’. Use the ‘this’ keyword instead.

Nguồn bài viết: Sưu tầm

BÌNH LUẬN BÀI VIẾT

Bài viết mới nhất

LIKE BOX

Bài viết được xem nhiều nhất

HỌC HTML