2013-04-22 42 views
6

Vì vậy, tôi có một hệ thống phân cấp đối tượng để tạo ra các điều khiển ui trong asp.net mvc và cố gắng đạt được một api thông thạo. Tôi làm một số lớp giả để tập trung vào vấn đề hiện tại.
Vì vậy, đây là "sai" codebase:Cách "ghi đè" các phương thức mở rộng trong .NET?

public abstract class HtmlElement { /* ... */ } 

public abstract class UIElement : HtmlElement { /* ... */ } 

public abstract class ButtonBase : UIElement { /* ... */ } 

public class LinkButton : ButtonBase { /* ... */ } 

public class ActionButton : ButtonBase { /* ... */ } 


public static class HtmlElementExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : HtmlElement 
    { 
    /* set the id */ 
    return item; 
    } 
} 

public static class ButtonBaseExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : ButtonBase 
    { 
    /* set the id and do some button specific stuff*/ 
    return item; 
    } 
} 

Khi tôi cố gắng gọi Id trên LinkButton trình biên dịch nói rằng có cuộc gọi mơ hồ:

LinkButton lb = new LinkButton().Id("asd"); 

Tôi thực sự nghĩ rằng trình biên dịch chọn khớp gần nhất trong trường hợp này vì vậy nếu tôi có một lớp Script kế thừa từ HtmlElement hơn phương thức HtmlExtensions Id được gọi, và cho một LinkButton (vì các hạn chế), phương thức ButtonBase sẽ được gọi. Tôi có một giải pháp, nhưng tôi không chắc là có giải pháp nào tốt hơn.
tôi xóa phương pháp Id từ ButtonBaseExtensions và sửa đổi phương pháp Id HtmlElementExtensions như cách thức sau đây:

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
    /* do some button specific stuff*/ 
    } 
    /* set the id */ 
    return item; 
} 

Bằng cách này mỗi hậu duệ lớp từ ButtonBase đang làm việc. Tôi không thực sự thích giải pháp của mình vì nó trộn lẫn logic HtmlElement với logic ButtonBase. Bất kỳ ý tưởng/đề xuất nào để có giải pháp tốt hơn? Tôi nghĩ tôi đặt chúng vào không gian tên khác nhau, nhưng chỉ trong một giây. Tôi cần phải sử dụng cả hai không gian tên, do đó, không giải quyết vấn đề.

Bạn có nghĩ rằng nó đáng nói đến trên diễn đàn msdn như một ý tưởng rằng trình biên dịch nên xem các hạn chế về các phương pháp mở rộng chung?

Trong khi chờ đợi tôi làm cho một số nghiên cứu thêm và bắt đầu một chủ đề trên các diễn đàn MSDN: link
Tôi đã thử một số methodds mở rộng không chung chung:

public class BaseClass { /*...*/ } 
    public class InheritedClass : BaseClass { /*...*/ } 

    public static class BaseClassExtensions 
    { 
    public static void SomeMethod(this BaseClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("BaseClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

    public static class InheritedClassExtensions 
    { 
    public static void SomeMethod(this InheritedClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("InheritedClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

Và nếu tôi nhanh chóng sau đây:

BaseClass bc = new BaseClass(); 
InheritedClass ic = new InheritedClass(); 
BaseClass ic_as_bc = new InheritedClass(); 

bc.SomeMethod("bc"); 
ic.SomeMethod("ic"); 
ic_as_bc.SomeMethod("ic_as_bc"); 

Sản xuất sản lượng này:

BaseClassExtensions.SomeMethod called wtih parameter: bc 
InheritedClassExtensions.SomeMethod called wtih parameter: ic 
BaseClassExtensions.SomeMethod called wtih parameter: ic_as_bc 

You can vote it for now

Cảm ơn,
Péter

+1

Một phương pháp mở rộng là ** không ** một thay thế cho một phương pháp ảo, cũng có thể nó được thực hiện ảo. Đó là khá không rõ ràng lý do tại sao bạn cần một, cho rằng bạn tự khai báo các lớp học này. Chỉ cần thêm một phương thức ảo vào một trong các lớp cơ sở và ghi đè lên nó, khi cần thiết, trong một lớp dẫn xuất. –

+0

Do api thông thạo, tôi phải sử dụng các phương pháp mở rộng. Các thừa kế generics chỉ làm việc với 2 mức độ thừa kế. Tôi đã viết một ví dụ với phương pháp mở rộng không thuộc về gen nơi trình biên dịch sử dụng hạn chế tham số để xác định đúng phương thức cần gọi. Tôi chỉ muốn trình biên dịch cũng đánh giá các hạn chế tham số chung. Tôi không thể viết "Id ButtonBase tĩnh công cộng (mục ButtonBase tĩnh này, id chuỗi)" thay vì "static T Id công cộng (mục T này, id chuỗi) trong đó T: ButtonBase" vì loại trả về sẽ là ButtonBase và ngắt chuỗi phương pháp của api thông thạo. –

Trả lời

2

Bạn có thể có một cái nhìn vào tài liệu MSDN về phương pháp khuyến nông: Extension Methods (C# Programming Guide). Phần thú vị là dưới Phương pháp mở rộng ràng buộc tại thời gian biên dịch:

... đầu tiên nó sẽ tìm kiếm kết quả phù hợp với phương pháp thể loại của loại. Nếu không tìm thấy kết quả phù hợp, nó sẽ tìm kiếm bất kỳ phương thức mở rộng nào được xác định cho loại và liên kết với phương thức mở rộng đầu tiên mà nó tìm thấy.

Vì vậy, đây là lý do bạn thấy hành vi này. Và tôi thực sự có thể mua nó, chỉ cần tưởng tượng rằng ai đó có thể ghi đè cách ứng dụng của bạn hoạt động với chỉ một phương thức public static T Id<T>(this T item, string id) where T : object. Và nếu bạn sẽ không thấy bất kỳ lỗi trình biên dịch nào, bạn sẽ nghĩ rằng mọi thứ đều chính xác và có thể mọi thứ sẽ hoạt động, ngoại trừ một số trường hợp hiếm khi xảy ra. Làm thế nào khó hiểu nó có thể được?

Và một điều xấu nữa về cách tiếp cận của bạn. Nếu tôi sẽ tiêu thụ API của bạn và sẽ thấy rằng đối với Nút Tôi có hai phương pháp: một trong số HtmlElementExtensions và một trong số ButtonBaseExtensions điều gì sẽ ngăn tôi làm việc này HtmlElementExtensions.Id(button, "id") thay vì ButtonExtensions.Id(button, "id")?

Trong trường hợp của bạn tôi muốn tiếp cận kết hợp:

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
     return (T)Id((ButtonBase)item); 
    } 
    else if (item is HtmlElement) 
    { 
     return (T)Id((HtmlElement)item); 
    } 

    throw new NotSupportedException("Type " + item.GetType() + " is not supported by Id extension method"); 
} 

private static ButtonBase Id(ButtonBase item, string id) 
{ 
    return item; 
} 

private static HtmlElement Id(HtmlElement item, string id) 
{ 
    return item; 
} 
+0

Với ví dụ của tôi, người dùng không thể ghi đè lên các phương thức của tôi, bởi vì trong cây thừa kế, ButtonBase gần gũi hơn với LinkButton hơn đối tượng. Và bạn cho rằng người dùng thực sự độc hại. Tôi nghĩ rằng nó đơn giản hơn nhiều để thêm ném ngoại lệ mới(); vào mã để tạo lỗi. : D Câu trả lời của bạn trông giống như giải pháp của tôi, chỉ cần một chút thanh lịch của bạn :) –

+0

@ Péter yep, xin lỗi. Tôi đã suy nghĩ về hướng khác nhau. Trong trường hợp nếu bạn sẽ có một 'ButtonBase' và ai đó sẽ viết phương thức mở rộng cho' LinkButton'. Và có bởi 'phương pháp kết hợp' tôi có nghĩa là cả hai giải pháp của bạn cùng một lúc;) – outcoldman

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