2009-07-15 25 views
38

Có quy tắc nào giúp xác định sử dụng phương pháp nào trong trường hợp nào không? Tôi có nên thích một trong những lần khác nhất không?Phương pháp mở rộng so với thừa kế

Cảm ơn!

+1

Cá nhân tôi, ít nhất gần đây, giống như các phương pháp mở rộng. Nhưng điều này là đúng đối với tôi bất cứ khi nào tôi học một cái gì đó mới - Tôi sử dụng nó liên tục một khi tôi tìm hiểu nó: x :) – Zack

Trả lời

26

Phương pháp mở rộng hữu ích, nhưng chúng khó phát hiện hơn thông qua IDE so với phương pháp thông thường vì chúng không được đính kèm với lớp gốc và không có manh mối nào về vị trí mã của chúng. Có một số best practice suggestions là nơi đặt chúng và cách đặt tên cho chúng, nhưng đây chỉ là hướng dẫn và không đảm bảo rằng ai đó sẽ theo dõi chúng.

Thông thường bạn sẽ sử dụng các phương pháp mở rộng nếu bạn chỉ thêm chức năng vào một lớp hoặc giao diện nổi tiếng, được sử dụng như lớp cơ sở .Net mà bạn không có quyền truy cập vào mã. Các phương thức mở rộng cũng có ràng buộc ở chỗ bạn không chỉ phải có assembly ban đầu, bạn phải có assembly với các phương thức mở rộng trong nó, mà phải được hiểu bởi người tiêu dùng mã của bạn.

Sử dụng kế thừa sẽ cho phép bạn thêm, xóa hoặc ghi đè chức năng và đảm bảo rằng nó luôn luôn hiện diện với lớp khi bạn tạo.

6

Bất cứ khi nào có thể, hãy sử dụng thừa kế thay vì phương pháp mở rộng.

chỉnh sửa

Tôi muốn giữ này ngắn và đơn giản, nhưng tôi sẽ tất nhiên câu trả lời câu hỏi tiếp theo.

Trong trường hợp thừa kế là có thể, đó là để nói rằng các lớp học không được niêm phong, nó hầu như luôn luôn là một lựa chọn tốt hơn so với phương pháp mở rộng. Trong thực tế, đây là những gì các best practices document mà womp tham chiếu nói. Nó có các tiêu đề như "Hãy thận trọng với các phương pháp mở rộng", "Hãy suy nghĩ kỹ trước khi mở rộng các loại bạn không sở hữu" và "Ưu tiên các tiện ích mở rộng giao diện trên các tiện ích mở rộng lớp học". Nói cách khác, nó chỉ nói những gì một lớp lót của tôi đã làm, với chi tiết hơn.

Bài viết không đưa ra lý do chi tiết, nhưng điểm mấu chốt là đây là cách các phương thức khuyến nông được thiết kế để sử dụng. Họ đã được thêm vào ngôn ngữ cuối trong trò chơi như một chút đường cú pháp để cho phép MS để nêm trong LINQ mà không cần phải quay trở lại và phát minh lại bánh xe. Đây là ví dụ kinh điển về những gì họ tốt. Một ví dụ điển hình là thêm phương pháp hữu ích, chẳng hạn như:

public static string FormatWith(this string format, params object[] args) 
{ return string.Format(CultureInfo.InvariantCulture, format, args); } 

Lưu ý rằng, trong trường hợp này, phương pháp khuyến nông là cách duy nhất để đạt được tính năng bổ sung này, vì chuỗi được niêm phong.

Đối với thành phần thừa kế, trong khi đây là một sự thật, tôi không thấy sự liên quan ở đây. Cho dù chúng tôi đang sử dụng các phương pháp mở rộng hay thừa kế, mục tiêu là thay đổi giao diện để cho phép một phương thức khác. Làm thế nào phương pháp này được thực hiện, cho dù theo thành phần, generics hoặc một số kỹ thuật khác, là trực giao.

+0

@Steven Tôi sẽ quan tâm đến lý do tại sao bạn nghĩ rằng? – kenny

+6

Thành phần ưu tiên hơn thừa kế. - http://www.artima.com/lejava/articles/designprinciples4.html –

+1

Với một lớp kín, bạn sẽ không có bất kỳ tùy chọn nào khác ngoài việc sử dụng các phương pháp mở rộng ...... –

2

Chúng rất khác nhau, ví dụ như các toán tử truy vấn chuẩn LINQ là ví dụ tuyệt vời về các phương pháp mở rộng nên khó thực hiện với thừa kế, nhưng nếu bạn có quyền truy cập vào lớp và có thể thay đổi nguồn thì sẽ tốt hơn khi sử dụng thừa kế,
EDIT
và đây là một số quy tắc mà tôi tìm thấy ở đây C# 3.0 Features: Extension Methods

  • phương pháp mở rộng không thể được sử dụng để ghi đè các phương pháp hiện có
  • một phương pháp khuyến nông có cùng tên và chữ ký như một phương pháp dụ sẽ không được gọi là
  • Không thể áp dụng khái niệm về các phương pháp mở rộng cho các trường, thuộc tính hoặc sự kiện
  • Sử dụng các phương pháp mở rộng ... sử dụng quá mức có thể là điều xấu!
13

Phương pháp mở rộng nên được sử dụng khi bạn muốn cung cấp triển khai trên nhiều loại khác nhau có cùng hành vi, nhưng nếu không sẽ không giống nhau. Đó là lý do tại sao bạn thấy các phương thức mở rộng được sử dụng trên các giao diện rất nhiều, bởi vì nó là một công cụ rất mạnh để đảm bảo rằng bất kỳ việc triển khai thực hiện nào của một giao diện sẽ có cùng thực hiện một hành vi nhất định.

Ví dụ: phương thức Bỏ qua và Mở rộng.

+0

hoặc một cái gì đó như LINQ, hoạt động trên nhiều loại (hoặc bộ sưu tập?)? – djcouchycouch

+1

@Steve Chính xác, Bỏ qua và Thực hiện cũng được triển khai cho LINQ. – Joseph

+0

Các phương thức Bỏ qua và Mở rộng nằm trong lớp System.Linq.Queryable, hoạt động trên giao diện IQueryable và trả về một IQueryable, làm cho chúng có thể được chuỗi, ví dụ Skip().Hãy() –

7

Vâng ... bạn không thể luôn luôn sử dụng kế thừa. Ví dụ: String là một lớp được niêm phong. Đó là trong những trường hợp mà một phương pháp mở rộng thực sự tỏa sáng.

Nói chung, các phương pháp mở rộng là tốt nhất cho các tiện ích nhỏ mà bạn có thể đặt vào một lớp tĩnh, nhưng hoạt động chống lại một thể hiện của một loại cụ thể. Các chuỗi là một ví dụ tuyệt vời - hầu hết mọi người đều có các phương thức mở rộng chuỗi nhỏ của riêng họ để thực hiện các thao tác nhỏ trên một chuỗi.

Một nơi tuyệt vời khác cho các phương pháp khuyến nông là chống lại các liệt kê. Tôi hầu như luôn luôn bao gồm phương pháp mở rộng HasFlag đối với bất kỳ số trang web nào mà tôi tạo ra [Flags].

+0

Ok, vậy điều này có liên quan gì đến bố cục? –

+0

Điều này không liên quan gì tới bố cục. Xem bình luận của tôi để trả lời của bạn. – Randolpho

2

Tôi sẽ dính vào thừa kế ngoại trừ trong trường hợp các phương pháp mở rộng được thiết kế chủ yếu cho - mở rộng các lớp được đóng dấu hoặc tạo các phần mở rộng phạm vi cụ thể. Cũng nên nhớ rằng chúng là tĩnh, vì vậy nếu đây là các phương thức và hành vi mà bạn sẽ cần phải ghi đè trong các lớp khác, bạn không thể thực sự sử dụng các phần mở rộng.

Phương pháp mở rộng có một tính năng thực sự tuyệt vời là lợi ích vốn có của việc triển khai. Vì chúng là các phương thức tĩnh, bạn có thể gọi chúng trên một đối tượng null.

Ví dụ:

string a = null; 
return a.IfNullOrEmpty("Default Value"); 

Triển khai như thế này là rất lớn, mặc dù họ có kỹ thuật chỉ đường cú pháp. IMHO, bất cứ thứ gì giúp mã của bạn sạch hơn và dễ đọc hơn rất tuyệt.

Mặc dù tôi không thích điều đó nhưng chúng thực sự không thể phát hiện được. Nếu tôi sao chép mã đó từ lớp này sang lớp khác, tôi sẽ phải tìm kiếm không gian tên mà nó được định nghĩa.

+0

Đó là một ví dụ điển hình. Trong thực tế, phương pháp mở rộng đầu tiên tôi đã viết là: public static bool IsNullOrEmpty (chuỗi này) {return string.IsNullOrEmpty (s); } –

+0

Hầu hết các tiện ích mở rộng mà tôi đã viết được thiết kế để tận dụng lợi thế của các đối tượng rỗng hoặc rỗng \ danh sách. Tôi thích tính năng đó. –

+0

Và đó là một ví dụ tốt về trường hợp thừa kế không phải là một tùy chọn. –

-1

Phương pháp mở rộng phá vỡ thiết kế OO tốt. Để nói rằng họ nên được sử dụng trên các lớp niêm phong mà bạn không có quyền truy cập vào các cơ sở mã là vô lý. Các lớp được niêm phong và bạn không có quyền truy cập vào có thể bị đóng kín vì một lý do (hiệu suất, an toàn luồng) và để gắn thẻ chức năng một cách mù quáng cho các lớp này là đúng nguy hiểm. Luôn có cách triển khai mẫu trang trí theo cách OO thuần túy và không làm theo cách đó làm cho mã khó đọc, duy trì và tái cấu trúc hơn. Theo quy tắc chung, nếu một tính năng của ngôn ngữ có mùi xấu thì bạn nên tránh. Tôi chắc rằng bạn có thể tìm thấy một ví dụ mà các phương pháp mở rộng hữu ích, tuy nhiên sự thật là tính năng này sẽ bị lạm dụng bởi những nhà phát triển có đào tạo OO tối thiểu.

+0

Tại sao các downvotes? –

+0

@AustinHenley có thể đào tạo OO tối thiểu? :) – ComeIn

0

Nó thực sự phụ thuộc vào vấn đề bạn cần giải quyết, trong hầu hết các trường hợp thừa kế lớp và giao diện tạo cảm giác tự nhiên hơn các phương pháp mở rộng và do đó nên được ưu tiên.

Mặt khác, Extensions cho phép bạn tạo các phương pháp hữu ích áp dụng không chỉ để một lớp - đó sẽ là nhiều cồng kềnh hơn để làm với thừa kế, nếu không nói là hầu như không thể đạt được.

Dưới đây là một số ví dụ mà phương pháp khuyến nông được sử dụng vì những lý do tốt:


LinqPad sử dụng phương pháp mở rộng, ví dụ như .Dump() phương pháp mà bạn có thể đổ (in) các nội dung của tất cả các loại đối tượng để cửa sổ đầu ra.


Khung NET chính nó sử dụng phương pháp mở rộng ở nhiều nơi, ví dụ như trong Linq:

public static TSource FirstOrDefault<TSource>(this 
         System.Collections.Generic.IEnumerable<TSource> source) 

mà trả về phần tử đầu tiên hoặc mặc định của bất kỳ đếm được bộ sưu tập của bất kỳ đối tượng kiểu.


Một ví dụ, nơi phương pháp khuyến nông là tốt hơn so với thừa kế như sau: Giả sử bạn muốn tạo ra một phương pháp có khả năng tạo ra một clone (bản sao) của bất kỳ đối tượng hiện có. Với phần mở rộng (và generics, cộng với sự phản ánh), bạn có thể làm điều đó this way.

Các vấn đề liên quan