Làm cách nào để mở rộng một lớp bằng các phương thức mở rộng c #?


98

Các phương thức mở rộng có thể được áp dụng cho lớp không?

Ví dụ: mở rộng DateTime để bao gồm một phương thức Tomorrow () có thể được gọi như:

DateTime.Tomorrow();

Tôi biết tôi có thể sử dụng

static DateTime Tomorrow(this Datetime value) { //... }

Hoặc là

public static MyClass {
  public static Tomorrow() { //... }
}

cho kết quả tương tự, nhưng làm cách nào tôi có thể mở rộng DateTime để tôi có thể gọi DateTime.Tomorrow?

Câu trả lời:


70

Bạn không thể thêm các phương thức vào một kiểu hiện có trừ khi kiểu hiện có được đánh dấu là một phần, bạn chỉ có thể thêm các phương thức có vẻ là thành viên của kiểu hiện có thông qua các phương thức mở rộng. Vì đây là trường hợp bạn không thể thêm các phương thức tĩnh vào chính kiểu vì các phương thức mở rộng sử dụng các thể hiện của kiểu đó.

Không có gì ngăn cản bạn tạo phương thức trợ giúp tĩnh của riêng bạn như thế này:

static class DateTimeHelper
{
    public static DateTime Tomorrow
    {
        get { return DateTime.Now.AddDays(1); }
    }
}

Mà bạn sẽ sử dụng như thế này:

DateTime tomorrow = DateTimeHelper.Tomorrow;

6
huh woot? trừ khi nó được thực hiện trong vòng 6 tháng kể từ ngày này và câu trả lời của Kumu ngay tại đó, điều này thực sự chưa hoàn thiện!
cregox

4
@Cawas cái này không phải là chưa hoàn chỉnh, Andrew đang chỉ cách thực hiện điều này với một trình trợ giúp tĩnh, không phải với một phương thức mở rộng (vì không có phiên bản).
Nick N.

1
Bạn nói đúng, Nick. Tôi thích các phương pháp mở rộng hơn! ;)
cregox

2
Còn extensionmethod.net/csharp/datetime là gì? IMHO, mẫu tốt hơn cho giảm thiểu đường cong học tập là ứng dụng thực tế với mã đầy đủ nguồn và mô hình tốt
Kiquenet

3
Vấn đề với mã này là nó chỉ hoạt động trên DateTime.Now chứ không phải bất kỳ đối tượng DateTime nào. Là một tiện ích, người ta có thể muốn sử dụng nó để xác định ngày sau một số ngày trước đó (hoặc trong tương lai). Chưa kể DateTime.Now được xác định mỗi khi bạn gọi nó là ...
MintGrowth

181

Sử dụng một phương pháp mở rộng .

Ví dụ:

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
            return date.AddDays(1);
        }    
    }
}

Sử dụng:

DateTime.Now.Tomorrow();

hoặc là

AnyObjectOfTypeDateTime.Tomorrow();

2
Shuggy câu trả lời 's cũng xóa sạch một số ánh sáng trên con đường tương tự để giải quyết này.
cregox

8
Đừng quên 'sử dụng ExtensionMethods;' ở đầu tài liệu của bạn cho điều này.
Luke Alderton

tại sao tôi không thể thực hiện DateTime.Tomorrow ()?
lawphotog

Chào lawphotog, phần mở rộng này cần một đối tượng, ở đây DateTime là một cấu trúc chứ không phải một đối tượng.
Kumu

4
Như đã đề cập trong các nhận xét trước đây (đối với tôi thì không đủ rõ ràng), bạn sẽ KHÔNG thể sử dụng DateTime.Tomorrow()vì các phương thức mở rộng chỉ hoạt động trên NÂNG CAO của một lớp và một cấu trúc lớp. Để "mở rộng" một phương thức tĩnh trên một chuỗi lớp, hãy làm theo câu trả lời của Andrew hoặc câu trả lời của Shuggy .
Alex

18

Các phương thức mở rộng là một đường cú pháp để tạo ra các phương thức tĩnh có tham số đầu tiên là một thể hiện của kiểu T trông như thể chúng là một phương thức thể hiện trên T.

Vì vậy, lợi ích phần lớn bị mất khi bạn thực hiện 'các phương thức mở rộng tĩnh' vì chúng có thể gây nhầm lẫn cho người đọc mã thậm chí nhiều hơn một phương thức mở rộng (vì chúng dường như đủ điều kiện nhưng không thực sự được xác định trong lớp đó) không có lợi về cú pháp (ví dụ có thể xâu chuỗi các cuộc gọi theo phong cách trôi chảy trong Linq).

Vì bạn sẽ phải đưa các tiện ích mở rộng vào phạm vi bằng cách sử dụng, tôi sẽ tranh luận rằng việc tạo đơn giản hơn và an toàn hơn:

public static class DateTimeUtils
{
    public static DateTime Tomorrow { get { ... } }
}

Và sau đó sử dụng điều này trong mã của bạn thông qua:

WriteLine("{0}", DateTimeUtils.Tomorrow)

11

Cách gần nhất mà tôi có thể đi đến câu trả lời là thêm một phương thức mở rộng vào một System.Typeđối tượng. Không đẹp, nhưng vẫn thú vị.

public static class Foo
{
    public static void Bar()
    {
        var now = DateTime.Now;
        var tomorrow = typeof(DateTime).Tomorrow();
    }

    public static DateTime Tomorrow(this System.Type type)
    {
        if (type == typeof(DateTime)) {
            return DateTime.Now.AddDays(1);
        } else {
            throw new InvalidOperationException();
        }
    }
}

Nếu không, IMO Andrew và ShuggyCoUk có cách triển khai tốt hơn.


Có vấn đề với cách tiếp cận này. Việc phải nhập "typeof (...)" là không thuận tiện, và với intellisense, bạn sẽ thấy các phần mở rộng thuộc mọi loại. Tuy nhiên, đó là một cách tiếp cận thú vị mà tôi chưa nghĩ đến, +1.
Meta-Knight

@ Meta-Knight Đúng, đó là lý do cá nhân tôi thích câu trả lời của người khác hơn. Câu trả lời của tôi sẽ có cú pháp gần nhất với câu hỏi OP, nhưng đó không phải là cách tốt nhất để giải quyết vấn đề này.
Adrian Godong

Typecó thể được thay thế bằng bất kỳ loại nào khác được yêu cầu. Tôi sử dụng nó với Fromvà nó hoạt động hoàn hảo. vì vậy tôi đoán câu trả lời này là chung chung nhưng đúng
Katia

3

Tôi cũng sẽ làm như Kumu

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
           return date.AddDays(1);
        }    
    }
}

nhưng gọi nó như thế này DateTime (). Tomorrow ();

Hãy nghĩ rằng nó tạo ra nhiều lượt xem hơn DateTime.Now.Tomorrow ();


1
Và bạn đã bỏ lỡ cơ hội viết nó dưới dạng nhận xét về câu trả lời của Kumu! : P
cregox

3

Chúng cung cấp khả năng mở rộng các loại hiện có bằng cách thêm các phương pháp mới mà không cần sửa đổi đối với loại. Gọi các phương thức từ các đối tượng của kiểu mở rộng trong một ứng dụng bằng cách sử dụng cú pháp phương thức thể hiện được gọi là phương thức '' mở rộng ''. Các phương thức mở rộng không phải là thành viên thể hiện trên loại. Điểm mấu chốt cần nhớ là các phương thức mở rộng, được định nghĩa là phương thức tĩnh, chỉ có trong phạm vi khi không gian tên được nhập rõ ràng vào mã nguồn ứng dụng của bạn thông qua chỉ thị using. Mặc dù các phương thức mở rộng được định nghĩa là phương thức tĩnh, chúng vẫn được gọi bằng cú pháp cá thể.

Kiểm tra ví dụ đầy đủ tại đây http://www.dotnetreaders.com/articles/Extension_methods_in_C-sharp.net,Methods_in_C_-sharp/201

Thí dụ:

class Extension
    {
        static void Main(string[] args)
        {
            string s = "sudhakar";
            Console.WriteLine(s.GetWordCount());
            Console.ReadLine();
        }

    }
    public static class MyMathExtension
    {

        public static int GetWordCount(this System.String mystring)
        {
            return mystring.Length;
        }
    }

3

Tôi đang tìm kiếm thứ gì đó tương tự - danh sách các ràng buộc trên các lớp cung cấp các Phương thức mở rộng. Có vẻ như rất khó để tìm một danh sách ngắn gọn nên đây là:

  1. Bạn không thể có bất kỳ thứ gì riêng tư hoặc được bảo vệ - các trường, phương thức, v.v.

  2. Nó phải là một lớp tĩnh, như trong public static class....

  3. Chỉ các phương thức mới có thể nằm trong lớp và tất cả chúng phải là public static.

  4. Bạn không thể có các phương thức tĩnh thông thường - những phương thức không bao gồm đối số này không được phép.

  5. Tất cả các phương pháp phải bắt đầu:

    public static ReturnType MethodName (ClassName _this này, ...)

Vì vậy, đối số đầu tiên luôn là tham chiếu này.

Có một vấn đề tiềm ẩn mà điều này tạo ra - nếu bạn thêm các phương thức yêu cầu khóa thuộc bất kỳ loại nào, bạn không thể thực sự cung cấp nó ở cấp lớp. Thông thường, bạn sẽ cung cấp một khóa cấp cá nhân riêng tư, nhưng không thể thêm bất kỳ trường riêng tư nào, để lại cho bạn một số tùy chọn rất khó xử, như cung cấp nó dưới dạng tĩnh công khai trên một số lớp bên ngoài, v.v. Hãy thực hiện. Dấu hiệu cho thấy ngôn ngữ C # đã có một bước ngoặt xấu trong thiết kế cho những thứ này .

Cách giải quyết là sử dụng lớp Phương thức mở rộng của bạn chỉ như một Mặt tiền cho một lớp thông thường và tất cả các phương thức tĩnh trong lớp Mở rộng của bạn chỉ gọi lớp thực, có thể là sử dụng Singleton .


2

Thật không may, bạn không thể làm điều đó. Tôi tin rằng nó sẽ hữu ích, mặc dù. Sẽ tự nhiên hơn khi gõ:

DateTime.Tomorrow

hơn:

DateTimeUtil.Tomorrow

Với một lớp Util, bạn phải kiểm tra sự tồn tại của một phương thức tĩnh trong hai lớp khác nhau, thay vì một.


1

Chúng tôi đã cải thiện câu trả lời của mình với phần giải thích chi tiết. Bây giờ, dễ hiểu hơn về phương pháp mở rộng

Phương thức mở rộng : Đó là một cơ chế mà qua đó chúng ta có thể mở rộng hành vi của lớp hiện có mà không cần sử dụng phân lớp con hoặc sửa đổi hoặc biên dịch lại lớp hoặc cấu trúc ban đầu.

Chúng tôi có thể mở rộng các lớp tùy chỉnh, các lớp khung .net, v.v.

Phương thức mở rộng thực sự là một loại phương thức tĩnh đặc biệt được định nghĩa trong lớp tĩnh.

DateTimelớp học đã được lấy ở trên và do đó chúng tôi không lấy lớp này để giải thích.

Dưới đây là ví dụ

// Đây là một lớp Máy tính hiện có chỉ có một phương thức (Thêm)

public class Calculator 
{
    public double Add(double num1, double num2)
    {
        return num1 + num2;
    }

}

// Below is the extension class which have one extension method.  
public static class Extension
{
    // It is extension method and it's first parameter is a calculator class.It's behavior is going to extend. 
    public static double Division(this Calculator cal, double num1,double num2){
       return num1 / num2;
    }   
}

// We have tested the extension method below.        
class Program
{
    static void Main(string[] args)
    {
        Calculator cal = new Calculator();
        double add=cal.Add(10, 10);
        // It is a extension method in Calculator class.
        double add=cal.Division(100, 10)

    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.