Bạn đã tìm thấy Ưu điểm nào của Phương pháp Mở rộng? [đóng cửa]


84

Một "người không tin tưởng" của C # đã hỏi tôi mục đích của các phương thức mở rộng là gì. Tôi đã giải thích rằng sau đó bạn có thể thêm các phương thức mới vào các đối tượng đã được xác định, đặc biệt khi bạn không sở hữu / kiểm soát nguồn đối với đối tượng ban đầu.

Anh ấy đưa ra "Tại sao không chỉ thêm một phương thức vào lớp của riêng bạn?" Chúng tôi đã đi vòng quanh (theo một cách tốt). Phản hồi chung của tôi là nó là một công cụ khác trong bộ công cụ, và câu trả lời của anh ấy là nó là một công cụ lãng phí vô ích ... nhưng tôi nghĩ tôi sẽ nhận được một câu trả lời "ngộ" hơn.

Một số tình huống mà bạn đã sử dụng các phương thức mở rộng mà bạn không thể (hoặc không nên có) sử dụng một phương thức được thêm vào lớp của riêng bạn là gì?


2
Tôi nghĩ rằng chắc chắn có những điểm hợp lệ mà mọi người có thể đưa ra (và đã thực hiện) để hỗ trợ các phương pháp mở rộng ... nhưng chắc chắn không có trường hợp nào mà người ta " không thể" sử dụng các phương thức tĩnh thay cho các phương thức mở rộng. Phương pháp khuyến nông thực sự chỉ phương pháp tĩnh, truy cập một cách khác nhau. Chỉ là một thứ để ghi nhớ trong đầu.
Dan Tao

@DanTao, lưu ý nhẹ hơn, một điều khiến cho việc gọi bằng phương thức mở rộng không thể thay thế được là cách đặt tên của chúng. Extensions.To(1, 10)là vô nghĩa và 1.To(10)mang tính mô tả. Tất nhiên tôi hiểu khía cạnh kỹ thuật mà bạn đang nói đến, chỉ nói. Trong thực tế, có những tình huống mà một người " không thể" sử dụng phương pháp mở rộng thay cho các phương thức tĩnh, ví dụ như phản ánh, một trường hợp khác là như vậy dynamic.
nawfal

Câu trả lời:


35

Tôi nghĩ rằng các phương thức mở rộng sẽ giúp ích rất nhiều khi viết mã, nếu bạn thêm các phương thức mở rộng vào các kiểu cơ bản, bạn sẽ nhận được chúng nhanh chóng trong intellisense.

Tôi có nhà cung cấp định dạng để định dạng kích thước tệp . Để sử dụng nó, tôi cần viết:

Console.WriteLine(String.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize));

Tạo một phương thức mở rộng mà tôi có thể viết:

Console.WriteLine(fileSize.ToFileSize());

Sạch hơn và đơn giản hơn.


4
một tên mô tả hơn cho phương thức tiện ích mở rộng của bạn sẽ làm cho nó vẫn rõ ràng hơn. ví dụ: fileSize.ToFormattedString ();
David Alpert

1
mmm, ToFormattedFizeSize () có lẽ, nó là một phương pháp khuyến nông cho một loại Long, ToFormattedString () không phải là một tên mô tả
Eduardo Campano

3
điểm thực hiện, mặc dù. khi tôi đọc fileSize.ToFileSize (), tôi hỏi tại sao có sự trùng lặp? nó thực sự đang làm gì? tôi biết rằng đó chỉ là một ví dụ, nhưng các tên mô tả giúp làm cho nó trở nên gọn gàng và đơn giản.
David Alpert

1
bạn đúng như với các bài kiểm tra, điều rất quan trọng là chọn đúng tên
Eduardo Campañó

5
Tuy nhiên, điều này không thực sự trả lời trực tiếp câu hỏi vì ví dụ bạn cung cấp không cần phải là một phương thức mở rộng. Bạn có thể đã có LongToFileSize(fileSize), điều này cũng ngắn gọn và rõ ràng như vậy.
Dan Tao

83

Các chỉ lợi thế của phương pháp khuyến nông là mã dễ đọc. Đó là nó.

Các phương thức mở rộng cho phép bạn thực hiện điều này:

foo.bar();

thay vì cái này:

Util.bar(foo);

Bây giờ có rất nhiều thứ trong C # giống như thế này. Nói cách khác, có nhiều tính năng trong C # có vẻ tầm thường và không mang lại lợi ích lớn cho bản thân. Tuy nhiên, một khi bạn bắt đầu kết hợp các tính năng này với nhau, bạn sẽ bắt đầu thấy thứ gì đó lớn hơn một chút so với tổng các phần của nó. LINQ được hưởng lợi rất nhiều từ các phương thức mở rộng vì các truy vấn LINQ sẽ gần như không thể đọc được nếu không có chúng. LINQ sẽ khả thi nếu không có các phương pháp mở rộng, nhưng không thực tế.

Các phương thức mở rộng rất giống các lớp từng phần của C #. Bản thân họ không hữu ích cho lắm và có vẻ tầm thường. Nhưng khi bạn bắt đầu làm việc với một lớp cần mã được tạo, các lớp từng phần bắt đầu có ý nghĩa hơn nhiều.


1
Điều này càng trở nên quan trọng nếu bạn đang sử dụng các phương thức chuỗi như x.Foo (something) .Bar (somethingelse) .Baz (yetmoresomething). Đây là prevalant nhất trong IEnumerable <> phương pháp khuyến nông và làm tăng đáng kể khả năng đọc
ShuggyCoUk

2
Điều tôi không hiểu là foo.bar () gợi ý cho tôi rằng bar () là một phương thức của foo. Vì vậy, khi nó không hoạt động, cuối cùng tôi đã tìm nhầm chỗ. Tôi không chắc đây thực sự là một cải tiến khả năng đọc đối với tôi.
Martin Brown

3
Nhấp chuột phải vào thanh () trong VS IDE và chọn Goto the Definition sẽ đưa bạn đến đúng nơi.
Jeff Martin

9
Ưu điểm duy nhất của 98% cấu trúc ngôn ngữ là khả năng đọc mã. Bất cứ điều gì đã qua một nhà điều hành chi nhánh, nhãn, goto và số gia đều ở đó để làm cho cuộc sống của bạn dễ dàng hơn một chút.
Giovanni Galbo

2
Điều đó không hoàn toàn đúng, có một số tình huống mà các phương thức mở rộng là cách duy nhất để mở rộng một lớp. Ngoài các lớp niêm phong, tôi sử dụng chúng trong một tình huống khá độc đáo. Xem bài viết của tôi dưới đây.
Edwin Jarvis

34

Đừng quên dụng cụ! Khi bạn thêm một phương thức mở rộng M vào kiểu Foo, bạn sẽ nhận được 'M' trong danh sách intellisense của Foo (giả sử lớp mở rộng nằm trong phạm vi). Điều này làm cho 'M' dễ tìm hơn nhiều so với MyClass.M (Foo, ...).

Vào cuối ngày, nó chỉ là đường cú pháp cho các phương thức tĩnh ở nơi khác, nhưng giống như mua một ngôi nhà: 'vị trí, vị trí, vị trí!' Nếu nó treo vào loại, mọi người sẽ tìm thấy nó!


2
Ngoài ra còn có một âm nhẹ như sau: phương pháp khuyến nông (chẳng hạn như những từ System.Linq điền lên IntelliSense autocomplete với một danh sách dài, làm cho nó một chút khó khăn hơn để điều hướng.
Jared Updike

@Jared: ... nhưng chỉ khi bạn đã nhập không gian tên nơi chứa các phương thức mở rộng, vì vậy bạn thực sự có một mức độ kiểm soát nhỏ đối với "ô nhiễm IntelliSense" này. Thứ hai, đừng quên rằng có các lớp trong BCL (chẳng hạn như String) đi kèm với một danh sách khá dài các phương thức thể hiện, vì vậy vấn đề này không dành riêng cho các phương thức mở rộng.
stakx - không còn góp

29

Hai lợi ích khác của các phương pháp mở rộng mà tôi đã tìm thấy:

  • Một giao diện thông thạo có thể được gói gọn trong một lớp tĩnh của các phương thức mở rộng, do đó đạt được sự tách biệt các mối quan tâm giữa lớp lõi và các phần mở rộng thông thạo của nó; Tôi đã thấy rằng nó đạt được khả năng bảo trì cao hơn.
  • Các phương thức mở rộng có thể bị treo khỏi các giao diện, do đó cho phép bạn chỉ định hợp đồng (thông qua một giao diện) và một loạt các hành vi dựa trên giao diện được liên kết (thông qua các phương thức mở rộng), một lần nữa cung cấp sự tách biệt các mối quan tâm. Một ví dụ là phần mở rộng LINQ phương pháp thích Select(...), Where(...)vv Hùng ra khỏi IEnumerable<T>giao diện.

1
Bạn có thể vui lòng trích dẫn một số ví dụ mã cho cả hai điểm để giúp họ hiểu rõ hơn không? Nó dường như là một quan điểm thú vị.
RBT

vâng, một ví dụ cho điểm thứ 2 sẽ rất hay.
Ini

26

Một số cách sử dụng tốt nhất mà tôi có đối với các phương pháp mở rộng là khả năng:

  1. Mở rộng chức năng trên các đối tượng của bên thứ ba (cho dù thương mại hoặc nội bộ của công ty tôi nhưng được quản lý bởi một nhóm riêng biệt), trong nhiều trường hợp sẽ được đánh dấu là sealed.
  2. Tạo chức năng mặc định cho các giao diện mà không cần phải triển khai một lớp trừu tượng

Lấy ví dụ IEnumerable<T>,. Mặc dù nó có nhiều phương thức mở rộng, nhưng tôi thấy khó chịu vì nó không triển khai một phương thức ForEach chung. Vì vậy, tôi đã tự tạo:

public void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
    foreach ( var o in enumerable )
    {
        action(o);
    }
}

Thì đấy, tất cả IEnumerable<T>các đối tượng của tôi bất kể kiểu triển khai, và liệu tôi có viết nó hay không hay ai đó đã có một ForEachphương thức bằng cách thêm một câu lệnh "using" thích hợp trong mã của tôi.


3
+1 Tôi thích tiện ích mở rộng này, tôi đã viết một tiện ích mở rộng giống như nó. Rất hữu ích
Maslow

10
Lưu ý rằng phương thức cần phải tĩnh. Tôi khuyên bạn nên đọc blog.msdn.com/ericlippert/archive/2009/05/18/… trước khi quyết định sử dụng tiện ích mở rộng này.
TrueWill

Lý do tại sao việc triển khai phương thức mở rộng ForEach trên IEnumerable không được đánh giá cao - blogs.msdn.microsoft.com/ericlippert/2009/05/18/…
RBT

12

Một trong những lý do tuyệt vời để sử dụng các phương pháp mở rộng là LINQ. Nếu không có các phương thức mở rộng thì rất nhiều việc bạn có thể làm trong LINQ sẽ rất khó. Các phương thức mở rộng Where (), Contains (), Select có nghĩa là nhiều chức năng hơn được thêm vào các kiểu hiện có mà không thay đổi cấu trúc của chúng.


4
nó không phải là 'một trong số', đó là lý do cho các phương pháp mở rộng.
gbjbaanb

3
Làm sao vậy? Đó là một trong nhiều lý do để có các phương pháp mở rộng, không phải là yêu cầu duy nhất. Tôi có thể sử dụng các phương pháp mở rộng mà không cần sử dụng LINQ.
Ray Booysen

Tôi nghĩ ý của anh ấy là lý do chúng được tạo ra nhưng vâng, anh ấy nói có vẻ như chúng là cách sử dụng thích hợp duy nhất cho chúng.
Brent Rittenhouse

9

Có rất nhiều câu trả lời về ưu điểm của các phương pháp mở rộng; làm thế nào về một giải quyết những bất lợi ?

Nhược điểm lớn nhất là không có lỗi hoặc cảnh báo trình biên dịch nếu bạn có một phương thức thông thường và một phương thức mở rộng có cùng chữ ký trong cùng một ngữ cảnh.

Giả sử bạn tạo một phương thức mở rộng áp dụng cho một lớp cụ thể. Sau đó, một người nào đó tạo một phương thức có chữ ký giống hệt trên chính lớp đó.

Mã của bạn sẽ được biên dịch và bạn thậm chí có thể không gặp lỗi thời gian chạy. Nhưng bạn không còn chạy mã giống như trước nữa.


1
4. Suy nghĩ kỹ trước khi mở rộng các loại bạn không sở hữu . Nếu bạn chỉ viết các phương thức mở rộng cho các loại bạn sở hữu, thì bạn không bao giờ phải lo lắng về việc tiện ích mở rộng của mình bị hỏng do các thay đổi đối với loại được mở rộng. Mặt khác, nếu bạn đang mở rộng các loại dân tộc khác với bạn về cơ bản là do lòng thương xót của họ.
DavidRR

1
... Ngoài ra, bạn có thể giảm thiểu rủi ro này bằng cách chọn những tên không có khả năng được người khác sử dụng hoặc bằng cách giảm thiểu tổng số phương pháp mở rộng mà bạn xác định (tức là giảm diện tích bề mặt). Một kỹ thuật để làm điều này có thể là viết một phương thức mở rộng để chuyển đổi đối tượng cơ bản sang một kiểu khác do bạn kiểm soát.
DavidRR

Các phương thức mở rộng (Hướng dẫn lập trình C #) : Nói chung, có thể bạn sẽ gọi các phương thức mở rộng thường xuyên hơn nhiều so với việc triển khai của riêng bạn.
DavidRR


4

Lập luận cá nhân của tôi cho các phương pháp Mở rộng là, chúng rất phù hợp với thiết kế OOP: hãy xem xét phương pháp đơn giản

bool empty = String.IsNullOrEmpty (myString)

so với

bool empty = myString.IsNullOrEmpty ();

Tôi không hiểu ví dụ của bạn có liên quan gì đến OOP. Ý anh là gì?
Andrew Hare

1
nó "có vẻ" rằng phương pháp này thuộc về các đối tượng
Bluenuance

3
Đây là một ví dụ tồi (có thể ném NullReferenceException) nhưng anh ấy đang đi đúng hướng. Một ví dụ tốt hơn có thể là thay thế Math.abs (-4) bằng một cái gì đó như -4.abs ();
Lập trình viên ngoài vòng pháp luật

15
theo hiểu biết của tôi (và theo kinh nghiệm của tôi, nếu bộ nhớ phục vụ), myString.IsNullOrEmpty () không cần ném NullReferenceException vì các phương thức mở rộng không yêu cầu một cá thể đối tượng để kích hoạt.
David Alpert

1
Cá nhân tôi không phải là người yêu thích các phương thức mở rộng nhằm làm việc với một phiên bản rỗng - nó trông giống như một việc đối với người đọc, nhưng sau đó lại làm một việc khác.
Mark Simpson

4

Có rất nhiều câu trả lời tuyệt vời ở trên về những gì các phương pháp mở rộng cho phép bạn làm.

Câu trả lời ngắn gọn của tôi là - họ gần như loại bỏ nhu cầu về nhà máy.

Tôi sẽ chỉ ra rằng chúng không phải là một khái niệm mới và một trong những xác nhận lớn nhất về chúng là chúng là một tính năng giết người trong Objective-C ( các danh mục ). Chúng bổ sung nhiều tính linh hoạt cho phát triển dựa trên khuôn khổ đến mức NeXT đã có NSA và các nhà lập mô hình tài chính Phố Wall làm người dùng chính.

REALbasic cũng thực hiện chúng như các phương pháp mở rộng và chúng đã được sử dụng tương tự ở đó đơn giản hóa việc phát triển.


4

Tôi muốn hỗ trợ các câu trả lời khác ở đây đề cập đến khả năng đọc mã được cải thiện như một lý do quan trọng đằng sau các phương pháp mở rộng. Tôi sẽ chứng minh điều này với hai khía cạnh của điều này: chuỗi phương thức so với các cuộc gọi phương thức lồng nhau và sự lộn xộn của một truy vấn LINQ với các tên lớp tĩnh vô nghĩa.


Hãy lấy truy vấn LINQ này làm ví dụ:

numbers.Where(x => x > 0).Select(x => -x)

Cả hai WhereSelectđều là các phương thức mở rộng, được định nghĩa trong lớp tĩnh Enumerable. Do đó, nếu các phương thức mở rộng không tồn tại và đây là các phương thức tĩnh bình thường, thì dòng mã cuối cùng về cơ bản sẽ phải trông như thế này:

Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x)

Xem truy vấn đó có gì hay hơn bao nhiêu.


Thứ hai, nếu bây giờ bạn muốn giới thiệu toán tử truy vấn của riêng mình, bạn sẽ tự nhiên không có cách nào xác định nó bên trong Enumerablelớp tĩnh, giống như tất cả các toán tử truy vấn tiêu chuẩn khác, bởi vì Enumerablenó nằm trong khuôn khổ và bạn không có quyền kiểm soát lớp đó. Do đó, bạn phải xác định lớp tĩnh của riêng mình chứa các phương thức mở rộng. Sau đó, bạn có thể nhận được các truy vấn như sau:

Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x)
//                ^^^^^^^^^^^^^^^^^^^^^^
//                different class name that has zero informational value
//                and, as with 'Enumerable.xxxxxx', only obstructs the
//                query's actual meaning.

3

Đúng là bạn có thể thêm phương thức (phần mở rộng) trực tiếp vào lớp của mình. Nhưng không phải tất cả các lớp đều do bạn viết. Các lớp từ thư viện lõi hoặc thư viện bên thứ ba thường bị đóng và sẽ không thể lấy được đường tổng hợp nếu không có các phương thức mở rộng. Nhưng hãy nhớ rằng, các phương thức mở rộng chỉ giống như các phương thức độc lập (tĩnh) trong ví dụ. c ++


3

Các phương thức mở rộng cũng có thể giúp giữ sạch các lớp và các lớp phụ thuộc của bạn. Ví dụ, bạn có thể cần một phương thức Bar () cho lớp Foo ở mọi nơi Foo được sử dụng. Tuy nhiên, bạn có thể muốn một phương thức .ToXml () trong một hợp ngữ khác và chỉ cho hợp ngữ đó. Trong trường hợp đó, bạn có thể thêm các phụ thuộc System.Xml và / hoặc System.Xml.Linq cần thiết vào assembly đó chứ không phải trong assembly gốc.

Lợi ích: sự phụ thuộc trong tập hợp lớp xác định của bạn được giảm xuống chỉ còn những nhu cầu cơ bản và các tập hợp tiêu thụ khác sẽ bị ngăn chặn sử dụng phương thức ToXml (). Xem bản trình bày PDC này để tham khảo thêm.


3

Tôi đồng ý rằng các phương thức mở rộng làm tăng khả năng đọc của mã, nhưng nó thực sự không có gì khác ngoài các phương thức trợ giúp tĩnh.

IMO sử dụng các phương thức mở rộng để thêm hành vi vào các lớp của bạn có thể là:

Khó hiểu: Các lập trình viên có thể tin rằng các phương thức là một phần của kiểu mở rộng, do đó không hiểu tại sao các phương thức lại biến mất khi không gian tên mở rộng không được nhập.

Một phản vật chất: Bạn quyết định thêm hành vi vào các loại trong khuôn khổ của mình bằng cách sử dụng các phương pháp mở rộng, sau đó chuyển chúng cho một người nào đó để kiểm tra đơn vị. Bây giờ anh ta bị mắc kẹt với một khuôn khổ chứa một loạt các phương pháp mà anh ta không thể giả mạo.


Không đúng khi một nhà phát triển bị mắc kẹt với một loạt các phương pháp mà anh ta không thể giả mạo. Các phương thức mở rộng này cuối cùng gọi một số phương thức trên lớp / giao diện mà chúng đang mở rộng và phương thức này có thể bị chặn nếu nó là ảo.
Patrik Hägne

1
Vâng, sau đó nó phụ thuộc vào những gì được thực hiện trong phương thức mở rộng. Giả sử bạn sử dụng các phương thức mở rộng được cung cấp trên một giao diện, thậm chí không biết chúng là các phương thức mở rộng. Sau đó, bạn sẽ không thể giả mạo một cuộc gọi phương thức đến một giao diện, nhưng bạn nhận ra rằng bạn thực sự đang gọi một phương thức tĩnh. Một cuộc gọi phương thức tĩnh không thể bị chặn bởi một API giả mạo dựa trên proxy, vì vậy tùy chọn duy nhất của bạn là phản ánh phương thức mở rộng để tìm ra phương thức nào thực sự giả mạo. Vẫn phản vật chất khi kết hợp với kiểm tra đơn vị IMO.
HaraldV

2

Các phương pháp mở rộng thực sự là sự kết hợp .NET của bộ tái cấu trúc "Giới thiệu phương pháp ngoại lai" từ Sách của Martin Fowler (xuống đến chữ ký phương pháp). Chúng đi kèm với những lợi ích và cạm bẫy về cơ bản giống nhau. Trong phần về trình tái cấu trúc này, anh ấy nói rằng chúng là một giải pháp thay thế khi bạn không thể sửa đổi lớp thực sự sở hữu phương thức.


2
+1, nhưng lưu ý rằng bạn cũng có thể sử dụng các phương thức mở rộng với giao diện.
TrueWill

2

Tôi chủ yếu xem các phương thức mở rộng như một sự thừa nhận mà có lẽ chúng không nên không cho phép các chức năng miễn phí.

Trong cộng đồng C ++, thông lệ OOP thường được coi là thích các hàm không phải là thành viên miễn phí hơn các thành viên, bởi vì các hàm này không phá vỡ tính đóng gói bằng cách giành quyền truy cập vào các thành viên riêng mà họ không cần. Các phương pháp mở rộng dường như là một cách đường vòng để đạt được điều tương tự. Đó là, một cú pháp rõ ràng hơn cho các hàm tĩnh không có quyền truy cập vào các thành viên riêng tư.

Các phương thức mở rộng không khác gì đường cú pháp, nhưng tôi không thấy có hại gì khi sử dụng chúng.


2
  • Intellisense trên chính đối tượng thay vì phải gọi một số hàm tiện ích xấu xí
  • Đối với các hàm chuyển đổi, có thể thay đổi "XToY (X x)" thành "ToY (X x này)", kết quả là x.ToY () đẹp thay vì XToY (x) xấu xí.
  • Mở rộng các lớp học mà bạn không có quyền kiểm soát
  • Mở rộng chức năng của các lớp khi không muốn thêm các phương thức vào chính các lớp đó. Ví dụ: bạn có thể giữ cho các đối tượng nghiệp vụ đơn giản và không có logic, đồng thời thêm logic nghiệp vụ cụ thể với các phụ thuộc xấu xí trong các phương thức mở rộng

2

Tôi sử dụng chúng để sử dụng lại các lớp mô hình đối tượng của mình. Tôi có một loạt các lớp đại diện cho các đối tượng mà tôi có trong cơ sở dữ liệu. Các lớp này được sử dụng ở phía máy khách chỉ để hiển thị các đối tượng nên cách sử dụng cơ bản là truy cập các thuộc tính.

public class Stock {
   public Code { get; private set; }
   public Name { get; private set; }
}

Do cách sử dụng đó, tôi không muốn có các phương thức logic nghiệp vụ trong các lớp này, vì vậy tôi đặt mọi logic nghiệp vụ thành một phương thức mở rộng.

public static class StockExtender {
    public static List <Quote> GetQuotesByDate(this Stock s, DateTime date)
    {...}
}

Bằng cách này, tôi có thể sử dụng các lớp tương tự để xử lý logic nghiệp vụ và để hiển thị giao diện người dùng mà không làm quá tải phía máy khách với mã không cần thiết.

Một điều thú vị về giải pháp này là các lớp mô hình đối tượng của tôi được tạo động bằng Mono.Cecil , vì vậy sẽ rất khó để thêm các phương thức logic nghiệp vụ ngay cả khi tôi muốn. Tôi có một trình biên dịch đọc các tệp định nghĩa XML và tạo các lớp sơ khai này đại diện cho một số đối tượng tôi có trong cơ sở dữ liệu. Cách tiếp cận duy nhất trong trường hợp này là mở rộng chúng.


1
Bạn có thể sử dụng các lớp học phần cho điều này ...
JJoos


1

Trong dự án cuối cùng của mình, tôi đã sử dụng phương thức mở rộng để đính kèm các phương thức Validate () vào các đối tượng nghiệp vụ. Tôi biện minh cho điều này bởi vì các đối tượng kinh doanh nơi các đối tượng truyền dữ liệu có thể tuần tự hóa và sẽ được sử dụng trong các miền khác nhau vì chúng là nơi các thực thể thương mại điện tử chung như sản phẩm, khách hàng, người bán, v.v. Cũng trong các miền khác nhau, các quy tắc kinh doanh cũng có thể khác nhau vì vậy tôi đã tóm tắt logic xác thực giới hạn muộn trong một phương thức Validate gắn liền với lớp cơ sở của các đối tượng truyền dữ liệu của tôi. Hy vọng điều này có ý nghĩa :)


1

Một trường hợp mà các phương pháp mở rộng khá hữu ích là trong một ứng dụng khách sử dụng dịch vụ web ASMX. Do tuần tự hóa, các loại phương thức web trả về không chứa bất kỳ phương thức nào (chỉ các thuộc tính công cộng của các loại này mới có sẵn trên máy khách).

Các phương thức mở rộng được phép sử dụng để thêm chức năng (ở phía máy khách) vào các kiểu được trả về bởi các phương thức web mà không cần phải tạo thêm một mô hình đối tượng khác hoặc nhiều lớp trình bao bọc ở phía máy khách.


1

Các phương pháp mở rộng có thể được sử dụng để tạo ra một loại hỗn hợp trong C #.

Đến lượt nó, điều này cung cấp sự tách biệt tốt hơn các mối quan tâm đối với các khái niệm trực giao. Hãy xem câu trả lời này làm ví dụ.

Điều này cũng có thể được sử dụng để kích hoạt các vai trò trong C #, một khái niệm trung tâm của kiến trúc DCI .


1

Cũng nên nhớ rằng các phương thức mở rộng đã được thêm vào như một cách để giúp truy vấn Linq dễ đọc hơn, khi được sử dụng trong kiểu C # của chúng.

Hai ảnh hưởng này hoàn toàn tương đương, nhưng ảnh hưởng đầu tiên dễ đọc hơn nhiều (và khoảng cách về khả năng đọc tất nhiên sẽ tăng lên khi có nhiều phương pháp được xâu chuỗi hơn).

int n1 = new List<int> {1,2,3}.Where(i => i % 2 != 0).Last();

int n2 = Enumerable.Last(Enumerable.Where(new List<int> {1,2,3}, i => i % 2 != 0));

Lưu ý rằng cú pháp đủ điều kiện thậm chí phải là:

int n1 = new List<int> {1,2,3}.Where<int>(i => i % 2 != 0).Last<int>();

int n2 = Enumerable.Last<int>(Enumerable.Where<int>(new List<int> {1,2,3}, i => i % 2 != 0));

Tình cờ, các tham số kiểu của WhereLastkhông cần phải được đề cập rõ ràng vì chúng có thể được suy ra nhờ sự hiện diện của tham số đầu tiên của hai phương thức này (tham số được giới thiệu bởi từ khóathis và làm cho chúng là phương thức mở rộng).

Điểm này rõ ràng là một lợi thế (trong số những điểm khác) của các phương thức mở rộng và bạn có thể hưởng lợi từ nó trong mọi tình huống tương tự có liên quan đến chuỗi phương thức.

Đặc biệt, đó là cách thanh lịch và thuyết phục hơn mà tôi tìm thấy để có một phương thức lớp cơ sở có thể bị xâm nhập bởi bất kỳ lớp con nào và trả về một tham chiếu được gõ mạnh cho lớp con này (với kiểu lớp con).

Ví dụ (ok, kịch bản này hoàn toàn sến sẩm): sau một đêm ngon giấc, một con vật mở mắt và kêu lên; mọi con vật đều mở mắt theo cùng một cách, ngược lại chó sủa và vịt kêu.

public abstract class Animal
{
    //some code common to all animals
}

public static class AnimalExtension
{
    public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return animal; //returning a self reference to allow method chaining
    }
}

public class Dog : Animal
{
    public void Bark() { /* ... */ }
}

public class Duck : Animal
{
    public void Kwak() { /* ... */ }
}

class Program
{
    static void Main(string[] args)
    {
        Dog Goofy = new Dog();
        Duck Donald = new Duck();
        Goofy.OpenTheEyes().Bark(); //*1
        Donald.OpenTheEyes().Kwak(); //*2
    }
}

Về mặt khái niệm OpenTheEyesnên là một Animalphương thức, nhưng sau đó nó sẽ trả về một thể hiện của lớp trừu tượng Animal, không biết các phương thức của lớp con cụ thể như BarkhoặcDuck hoặc bất cứ điều gì. 2 dòng nhận xét là * 1 và * 2 sau đó sẽ phát sinh lỗi biên dịch.

Nhưng nhờ các phương thức mở rộng, chúng ta có thể có một loại "phương thức cơ sở biết loại lớp con mà nó được gọi".

Lưu ý rằng một phương pháp chung đơn giản có thể đã thực hiện công việc, nhưng theo một cách khó xử hơn nhiều:

public abstract class Animal
{
    //some code common to all animals

    public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
    {
        //Some code to flutter one's eyelashes and then open wide
        return (TAnimal)this; //returning a self reference to allow method chaining
    }
}

Lần này, không có tham số và do đó không có suy luận kiểu trả về có thể có. Cuộc gọi không thể là gì khác hơn là:

Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();

... có thể làm nặng mã rất nhiều nếu có nhiều chuỗi liên quan hơn (đặc biệt khi biết rằng tham số kiểu sẽ luôn nằm <Dog>trên dòng của Goofy và <Duck>trên dòng của Donald ...)


1
Lần đầu tiên tôi thấy "kwak". Đây có phải là cách đánh vần phổ biến ở nước khác không? Cách duy nhất tôi từng thấy nó được đánh vần là "quack".
Brent Rittenhouse

0

Tôi chỉ có một từ để nói về nó: DUY TRÌ, đây là chìa khóa để sử dụng các phương pháp mở rộng


4
Tôi nghĩ bạn có thể cần nhiều hơn một từ vì tôi không hiểu điều này có nghĩa là gì.
Lập trình viên ngoài vòng pháp luật

2
Tôi thực sự sẽ nói rằng đó là một lập luận chống lại các phương pháp mở rộng. Một trong những rủi ro của các phương pháp mở rộng là chúng có thể ở khắp mọi nơi, do mọi người tạo ra. Bệnh dại có thể xảy ra nếu không được sử dụng cẩn thận.
Boris Callens

Bạn có thể dễ dàng tìm thấy tất cả các tài liệu tham khảo khi đang sử dụng EM. Ngoài ra, để thay đổi trên toàn cầu một phương pháp, bạn có thể làm điều đó từ một nơi duy nhất
Tamir

0

Tôi nghĩ rằng các phương pháp mở rộng giúp viết mã rõ ràng hơn.

Thay vì đặt một phương thức mới bên trong lớp của bạn, như bạn bè của bạn đề xuất, bạn đặt nó vào không gian tên ExtensionMethods. Bằng cách này, bạn duy trì một cảm giác hợp lý về trật tự cho lớp học của mình. Các phương thức không thực sự xử lý trực tiếp với lớp của bạn sẽ không làm xáo trộn nó.

Tôi cảm thấy các phương pháp mở rộng làm cho mã của bạn rõ ràng hơn và được tổ chức hợp lý hơn.


0

Nó cho phép trình soạn thảo / IDE của bạn tự động hoàn thành đề xuất một cách thông minh.


0

Tôi yêu họ vì đã xây dựng html. Thường xuyên có những phần được sử dụng lặp đi lặp lại hoặc được tạo đệ quy trong đó một hàm hữu ích nhưng nếu không sẽ phá vỡ quy trình của chương trình.

        HTML_Out.Append("<ul>");
        foreach (var i in items)
            if (i.Description != "")
            {
                HTML_Out.Append("<li>")
                    .AppendAnchor(new string[]{ urlRoot, i.Description_Norm }, i.Description)
                    .Append("<div>")
                    .AppendImage(iconDir, i.Icon, i.Description)
                    .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("</div></li>");
            }

        return HTML_Out.Append("</ul>").ToString();

Cũng có những trường hợp mà một đối tượng cần logic tùy chỉnh để được chuẩn bị cho các phương thức mở rộng đầu ra HTML cho phép bạn thêm chức năng này mà không trộn lẫn giữa trình bày và logic trong lớp.


0

Tôi thấy rằng các phương thức mở rộng rất hữu ích để khớp với các đối số chung được lồng vào nhau.

Điều đó nghe có vẻ hơi ngớ ngẩn - nhưng giả sử chúng ta có một lớp chung MyGenericClass<TList>và chúng ta biết rằng bản thân TList là chung (ví dụ:List<T> ), tôi không nghĩ rằng có cách nào để tìm ra chữ 'T' lồng nhau đó từ Danh sách mà không có phần mở rộng phương thức hoặc phương thức trợ giúp tĩnh. Nếu chúng ta chỉ có các phương thức trợ giúp tĩnh theo ý của chúng ta, thì nó (a) xấu và (b) sẽ buộc chúng ta di chuyển chức năng thuộc về lớp đó ra một vị trí bên ngoài.

Ví dụ: để truy xuất các kiểu trong một bộ tuple và chuyển đổi chúng thành một chữ ký phương thức, chúng ta có thể sử dụng các phương thức mở rộng:

public class Tuple { }
public class Tuple<T0> : Tuple { }
public class Tuple<T0, T1> : Tuple<T0> { }

public class Caller<TTuple> where TTuple : Tuple { /* ... */ }

public static class CallerExtensions
{
     public static void Call<T0>(this Caller<Tuple<T0>> caller, T0 p0) { /* ... */ }

     public static void Call<T0, T1>(this Caller<Tuple<T0, T1>> caller, T0 p0, T1 p1) { /* ... */ }
}

new Caller<Tuple<int>>().Call(10);
new Caller<Tuple<string, int>>().Call("Hello", 10);

Điều đó nói rằng, tôi không chắc chắn đường phân chia nên ở đâu - khi nào thì một phương thức nên là một phương thức mở rộng và khi nào nó nên là một phương thức trợ giúp tĩnh? Có suy nghĩ gì không?


0

Tôi có các vùng nhập liệu trên màn hình của mình và tất cả đều phải triển khai một hành vi tiêu chuẩn cho dù loại chính xác của chúng là gì (hộp văn bản, hộp kiểm, v.v.). Chúng không thể kế thừa một lớp cơ sở chung vì mỗi loại vùng đầu vào đã xuất phát từ một lớp cụ thể (TextInputBox, v.v.)

Có thể bằng cách đi lên trong hệ thống phân cấp kế thừa, tôi có thể tìm thấy một tổ tiên chung như WebControl đã nói, nhưng tôi đã không phát triển lớp khung công tác WebControl và nó không hiển thị những gì tôi cần.

Với phương thức mở rộng, tôi có thể:

1) mở rộng lớp WebControl và sau đó có được hành vi tiêu chuẩn thống nhất của tôi trên tất cả các lớp đầu vào của tôi

2) cách khác làm cho tất cả các lớp của tôi bắt nguồn từ một giao diện, chẳng hạn như IInputZone, và mở rộng giao diện này bằng các phương thức. Bây giờ tôi sẽ có thể gọi các phương thức tiện ích mở rộng liên quan đến giao diện trên tất cả các vùng nhập của tôi. Do đó, tôi đã đạt được kiểu đa kế thừa vì các vùng đầu vào của tôi đã có nguồn gốc từ nhiều lớp cơ sở.


0

Có rất nhiều ví dụ tuyệt vời về các phương thức mở rộng..đặc biệt trên IEnumerables như đã đăng ở trên.

ví dụ: nếu tôi có, IEnumerable<myObject>tôi có thể tạo và mở rộng phương thức choIEnumerable<myObject>

mylist List<myObject>;

... tạo danh sách

mylist.DisplayInMyWay();

Nếu không có Phương thức mở rộng sẽ phải gọi:

myDisplayMethod(myOldArray); // can create more nexted brackets.

một ví dụ tuyệt vời khác là tạo Danh sách liên kết vòng trong nháy mắt!

Tôi có thể ghi công cho nó!

danh sách liên kết hình tròn sử dụng các Phương pháp mở rộng

Bây giờ kết hợp chúng và sử dụng phần mở rộng Phương thức đọc mã như sau.

myNode.NextOrFirst().DisplayInMyWay();

hơn là

DisplayInMyWay(NextOrFirst(myNode)).

sử dụng các phương pháp mở rộng Nó gọn gàng hơn, dễ đọc hơn và hướng đối tượng hơn. cũng rất gần với:

myNode.Next.DoSomething()

Cho đồng nghiệp của bạn xem! :)

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.