2011-01-07 8 views
8

tôi đã xác định các lớp và các phương pháp sau đây:Tại sao C# không ràng buộc đúng với phương pháp ghi đè chung?

using System; 
using System.Linq.Expressions; 
using System.Windows.Forms; 

public class ReturnValue<T, S> {} 

public class Something<T> 
{ 
    // Sorry about the odd formatting. Trying to get it to fit nicely... 
    public ReturnValue<T, C> 
    Do<C, S>(C control, Expression<Func<C, S>> controlProperty) 
    where C : Control 
    { 
     return new ReturnValue<T, C>(); 
    } 

    public ReturnValue<T, ToolStripItem> 
    Do<S>(ToolStripItem control, Expression<Func<ToolStripItem, S>> controlProperty) 
    { 
     return new ReturnValue<T, ToolStripItem>(); 
    } 
} 

này biên dịch tốt. Woo hoo! Nửa đường. Sau đó, tôi cố gắng sử dụng nó sau này với mã như thế này:

var toolStripItem = new ToolStripStatusLabel(); 

var something = new Something<string>(); 
something.Do(toolStripItem, t => t.Text); // Does not compile 

này, tuy nhiên, chết với thông báo lỗi sau

Loại ToolStripStatusLabel không thể được sử dụng như loại tham số C trong các loại generic hoặc phương pháp Something<T>.Do<C,S>(C, Expression<Func<C,S>>). Không có chuyển đổi tham chiếu ngầm từ ToolStripStatusLabel đến Control.

Dường như với tôi trình biên dịch C# đã thất bại trong trường hợp này mặc dù hai phương pháp không tạo ra một bộ khai báo phương thức không rõ ràng. ControlToolStripStatusLabel tồn tại như anh chị em trong cây thừa kế của Component. Tôi nghĩ rằng trình biên dịch sẽ có đủ thông tin để ràng buộc chính xác lời gọi phương thức trong mã máy khách.

Tuy nhiên, nếu tôi làm điều tương tự với các lớp anh chị em của riêng tôi, thì mọi thứ sẽ biên soạn tốt.

public class Parent {} 
public class Child1 : Parent {} 
public class Child2 : Parent {} 

public class Something2<T> 
{ 
    public ReturnValue<T, C> 
    Do<C, S>(C control, Expression<Func<C, S>> controlProperty) 
    where C : Child1 
    { 
     return new ReturnValue<T, C>(); 
    } 

    public ReturnValue<T, Child2> 
    Do<S>(Child2 control, Expression<Func<Child2, S>> controlProperty) 
    { 
     return new ReturnValue<T, Child2>(); 
    } 
} 

var child2 = new Child2(); 
var something2 = new Something2<string>(); 
something2.Do(child2, c => c.GetType()); // Compiles just fine 

Mọi người có thể làm sáng tỏ những gì tôi đã làm sai, nếu có gì không?

Trả lời

11

Vấn đề là phương pháp đầu tiên trong ứng cử viên thiết để giải quyết tình trạng quá tải, vì kiểu chế C : Control chỉ áp dụng sau độ phân giải quá tải đã được thực hiện. Tôi tin rằng bạn hy vọng nó sẽ được loại bỏ sớm - và nó không phải là.

Bây giờ, nếu bạn xử lý C = ToolStripItem, quá tải đầu tiên cụ thể hơn lần thứ hai - do đó kết quả của quá trình phân giải quá tải là chọn phiên bản đầu tiên đó.

Xác thực ràng buộc loại là sau đó được áp dụng ... và không thành công.

Tôi có blog post on this matter có thể giúp bạn hiểu quy trình và sau đó another blog post nơi tôi áp dụng các quy tắc theo cách khá ngớ ngẩn.

EDIT: Trong ví dụ thứ hai của bạn, loại đối số là chính xác loại được chỉ định trong thông số đầu tiên, vì vậy phương pháp đầu tiên không kết thúc cụ thể hơn. Phương pháp thứ hai thắng do có ít tham số kiểu (tôi nghĩ; tôi chưa kiểm tra chi tiết) và sau đó được xác thực và vượt qua.

Để đưa nó trở lại vào các điều khoản ToolStripItem, bạn thực sự có thể làm cho mẫu đầu tiên bạn biên dịch với một thay đổi đơn giản:

// Change this 
var toolStripItem = new ToolStripStatusLabel(); 
// To this... 
ToolStripItem toolStripItem = new ToolStripStatusLabel(); 

Thay đổi kiểu thời gian biên dịch toolStripItem từ ToolStripStatusLabel để ToolStripItem mất đi những "lợi thế" rằng phương pháp đầu tiên đã có, do đó, nó sau đó biên dịch.

+0

Jon, đó là một lời giải thích tuyệt vời. Cảm ơn bạn. – realistschuckle

+0

Vậy tại sao ví dụ thứ hai lại biên dịch? –

+0

BlueRaja (bên cạnh một tên tuyệt vời) viết chính xác, mà tôi đã nghĩ đến sau khi xem xét giải thích thêm. Có hiểu biết gì không, Jon? – realistschuckle

0

Tôi nghĩ rằng bạn chỉ cần phải rõ ràng hơn với cuộc gọi của bạn:

var toolStripItem = new ToolStripStatusLabel(); 
var something = new Something<string>(); 
something.Do<string>(toolStripItem, t => t.Text); // might compile