2011-11-08 35 views
11

Tôi gặp sự cố khi tải trình biên dịch C# để gọi một phương thức mở rộng mà tôi đã tạo, vì nó thích một phương thức thể hiện với đối số params thay thế.Làm thế nào để buộc sử dụng phương pháp mở rộng thay vì phương pháp dụ với thông số?

Ví dụ, nói rằng tôi có lớp sau và phương pháp của nó:

public class C 
{ 
    public void Trace(string format, params object[] args) 
    { 
     Console.WriteLine("Called instance method."); 
    } 
} 

Và và mở rộng:

public static class CExtensions 
{ 
    public void Trace(this C @this, string category, string message, params Tuple<string, decimal>[] indicators) 
    { 
     Console.WriteLine("Called extension method."); 
    } 
} 

Trong chương trình mẫu của tôi:

pubic void Main() 
{ 
    var c = new C(); 

    c.Trace("Message"); 
    c.Trace("Message: {0}", "foo"); 
    c.Trace("Category", "Message", new KeyValuePair<string, decimal>("key", 123)); 
} 

Tất cả gọi in Called instance method..

Tôi không có quyền truy cập vào lớp C, rõ ràng hoặc tôi sẽ không bận tâm đến việc tạo các phương thức tiện ích và tiện ích mở rộng của tôi quan trọng vì nó sẽ cho phép người dùng của tôi tiếp tục sử dụng một lớp học mà họ đã biết.

Từ những gì tôi đã hiểu, trình biên dịch sẽ ưu tiên các phương pháp ví dụ trên các phương pháp mở rộng, nhưng đây có phải là quy tắc duy nhất không? Điều đó có nghĩa là bất kỳ lớp nào có phương thức giống như Method(string format, params object[] args) không thể có các phương thức mở rộng với tham số đầu tiên thuộc loại string.

Bất kỳ lời giải thích nào về lý do cho hành vi này hoặc cách giải quyết nó (nghĩa là không phải "chỉ cần gọi CExtensions.Trace(c, "Category", ...") sẽ được đánh giá cao.

+0

Tôi đã xem xét điều này trước đây. Dường như giải pháp duy nhất là đổi tên phương thức. Tôi muốn được quan tâm để xem nếu có một sửa chữa. – Polynomial

Trả lời

5

Bạn không thể sử dụng tiện ích mở rộng để "tiếp quản" các phương thức lớp hiện có.

Nếu cuộc gọi hoạt động mà không có phần mở rộng, hành vi sẽ không thay đổi khi bạn thêm tiện ích. Lý do cho điều này là mã hiện tại không nên phá vỡ bằng cách giới thiệu một phần mở rộng sau này.

Bạn phải sử dụng tên khác cho tên đó hoặc sử dụng các loại thông số khác với phương pháp lớp.

+0

Mặc dù nhiều người đưa ra gợi ý tốt về cách giải quyết (mặc dù không có cách nào giải quyết được cho tôi) Tôi xem đây là câu trả lời hay nhất vì nó tóm tắt hoàn hảo vấn đề của tôi xuất phát từ đâu. – madd0

4

Bạn không thể trực tiếp. Phương pháp trên cá thể đích luôn được ưu tiên hơn một phương thức mở rộng. Các chỉ cách để làm điều này (trong khi vẫn giữ tên giống nhau, vv) là sử dụng phương pháp CExtensions.Trace (trong câu hỏi).

Trong một số trường hợp, một trick ở đây sẽ được sử dụng một số cơ sở hạng của C hoặc giao diện mà C dụng cụ, nhưng mà không có một phương pháp Trace, và nhập lại biến, và thêm một tình trạng quá tải về CExtensions, tức là

IFoo foo = c; 
foo.Trace(...); 
+0

Tôi thích mẹo thêm phương thức mở rộng vào loại cơ sở. Tất nhiên, điều đó sẽ quá dễ dàng và, trong trường hợp của tôi, tôi sẽ phải mở rộng 'đối tượng' ... – madd0

0

tôi đoán bạn có thể thay đổi loại tham số đầu tiên, hoặc tên hàm (TraceFormat?)

này params phiên bản trông rất tham lam, vì vậy nếu bạn duy trì số đầu tiên như str ing nó sẽ luôn luôn bắt tất cả các cuộc gọi và bỏ qua phương pháp mở rộng tôi tin.

+0

Tôi thực sự không muốn thay đổi tên phương thức, bởi vì đó là điều mà các nhà phát triển đã quen . Thay đổi tên có nghĩa là phương pháp sẽ không được sử dụng như chúng tôi muốn. Thay đổi kiểu của đối số đầu tiên thành một cái gì đó khác với 'chuỗi' sẽ là khó khăn, vì tôi thực sự muốn một tên danh mục như là một' chuỗi'. – madd0

+0

đối số đầu tiên thực sự là âm thanh Danh mục giống như nó có thể là một ứng cử viên tốt để trở thành một Enum ví dụ và nó sẽ bắt đầu làm việc. –

+0

Valentin, hãy thử, nhưng đối với một số nhóm, "danh mục" có thể là tên của khách hàng. Vì tôi thực sự hy vọng cơ sở khách hàng của chúng tôi tiếp tục phát triển, tôi muốn tránh một 'enum' mà sẽ phải được cập nhật liên tục;) – madd0

2

Tuple<string, decimal> không giống như KeyValuePair<string, decimal>. Do đó KeyValuePair<string, decimal> được truyền vào được lấy như một đối tượng do đó phương thức thành viên với params object[] args được sử dụng.

Trên thực tế KeyValuePair là cấu trúc .

+0

'Tuple' hoặc' KeyValuePair', nó không thực sự thay đổi nhiều, cả hai loại _any_ type cho rằng vấn đề — sẽ được "hấp thụ" bởi mảng 'object'. – madd0

+0

Đó là sự thật, nhưng nếu bạn sử dụng một 'Tuple ' trong cuộc gọi nó sẽ vẫn không sử dụng phương pháp mở rộng. – Guffa

+0

Có, tôi vừa thử nghiệm và không sử dụng nó. – Aliostad

2

Bạn có thể làm điều đó với lý lẽ tên:

c.Trace(
    "Category", 
    "Message", 
    indicators: new Tuple<string, decimal>("key", 123)); 

nhưng bạn mất các chức năng params và bạn sẽ cần phải vượt qua một cách rõ ràng một mảng cho đối số chỉ số, như sau:

c.Trace(
    "Category", 
    "Message", 
    indicators: new Tuple<string, decimal>[] 
    { 
     new Tuple<string, decimal>("key", 123), 
     new Tuple<string, decimal>("key2", 123) 
    }); 
+0

nó sẽ làm việc, nhưng nó dễ dàng để quên đối số được đặt tên này và chúng tôi sẽ âm thầm gọi phiên bản mặc định mà không có cách nào để nhận thấy điều này. –

+0

@Valentin Kuzub, nhưng các tùy chọn khác cũng dễ bị lỗi. Bạn có thể quên bạn cần gọi phương thức với tên khác hoặc chữ ký khác. –

+0

Đối số của João chính là lý do tại sao tôi không muốn thay đổi tên hoặc chữ ký của phương thức, nhưng quyền Valentin: rất có khả năng người ta sẽ quên sử dụng đối số được đặt tên và gọi phiên bản mặc định mà không bao giờ chú ý (hoặc chỉ khi quá muộn). – madd0

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