2015-04-24 17 views
7

Với này "IHandle" giao diện và hai lớp được xử lý:Odd C# hành vi khi thực hiện giao diện chung

interface IHandle<T> 
{ 
    void Handle(T m); 
} 


class M1 
{ 
    public int Id; 
} 


class MReset 
{ 
} 

Tôi muốn tạo ra một cơ sở chung mà sẽ chăm sóc của "thiết lập lại" cũng như quản lý các trường hợp M1 :

class HandlerBase<T> : 
    IHandle<MReset>, 
    IHandle<T> where T : M1 
{ 
    protected int Count; 

    void IHandle<T>.Handle(T m) 
    { 
    ++Count; 
    Console.WriteLine("{0}: Count = {0}", m.Id, Count); 
    } 


    void IHandle<MReset>.Handle(MReset m) 
    { 
    Count = 0; 
    } 
} 

này không biên dịch từ trình biên dịch tin T có thể là "MReset" nên kết quả đầu ra:

lỗi CS0 695: 'HandlerBase' không thể thực hiện cả hai 'IHandle' và 'IHandle' bởi vì họ có thể thống nhất đối với một số tham số kiểu thay

Điều đó tự nó là hơi kỳ lạ kể từ khi tôi không thể nhìn thấy như thế nào T có thể có thể là loại MReset vì nó phải thuộc loại M1. Nhưng không sao, tôi có thể chấp nhận rằng trình biên dịch là thông minh hơn tôi :-)

Sửa: Trình biên dịch không phải là thông minh hơn tôi :-) Theo một bình luận trên Why does this result in CS0695? chúng ta có "tuyên bố Hạn chế không được xem xét khi xác định tất cả các loại có thể xây dựng ".

Bây giờ tôi trao đổi các tờ khai giao diện:

class HandlerBase<T> : 
    IHandle<T> where T : M1, 
    IHandle<MReset> 
{ 
    ... same as before .. 
} 

Và đột nhiên tôi nhận được một thông báo lỗi khác nhau nói rằng tôi không thể thực hiện IHandle.Handle (MReset m) kể từ khi khai báo lớp không nêu rằng nó được thực hiện mà giao diện:

lỗi CS0540: '. HandlerBase.IHandle < ...> Xử lý (MReset): chứa loại không thực hiện giao diện 'IHandle'

Câu hỏi: tại sao thứ tự khai báo tạo sự khác biệt như vậy? Chuyện gì xảy ra trong ví dụ thứ hai?

Trong khi kết thúc nó quay ra rằng có một giải pháp:

class HandlerBase : 
    IHandle<MReset> 
{ 
    protected int Count; 


    void IHandle<MReset>.Handle(MReset m) 
    { 
    Count = 0; 
    } 
} 


class Handler<T> : HandlerBase, 
    IHandle<T> where T : M1 
{ 
    void IHandle<T>.Handle(T m) 
    { 
    ++Count; 
    Console.WriteLine("{0}: Count = {0}", m.Id, Count); 
    } 
} 

Nhưng giải pháp duy nhất hoạt động nếu HandlerBase cụ IHandle<MReset> - không nếu giao diện chung IHandle<T> được thực hiện trong HandlerBase đầu tiên. Tại sao?

Sửa: Thực hiện IHandle<T> trong HandlerBasekhông làm việc (và nếu tôi đã cho thấy mã ai đó có thể đã nhìn thấy nó). Đây hoạt động:

class HandlerBase<T> : 
    IHandle<T> where T : M1 
{ 
    protected int Count; 

    void IHandle<T>.Handle(T m) 
    { 
    ++Count; 
    Console.WriteLine("Type = {0}, Id = {1}, Count = {2}", GetType(), m.Id, Count); 
    } 
} 


class Handler<T> : HandlerBase<T>, 
    IHandle<MReset> 
    where T : M1 
{ 
    void IHandle<MReset>.Handle(MReset m) 
    { 
    Count = 0; 
    Console.WriteLine("RESET"); 
    } 
} 

Đáng tiếc là khai báo lớp thứ hai của tôi là thế này:

class Handler<T> : HandlerBase<T> where T : M1, 
    IHandle<MReset> 
{ 
    void IHandle<MReset>.Handle(MReset m) 
    { 
    Count = 0; 
    Console.WriteLine("RESET"); 
    } 
} 

Chú ý sự khác biệt tinh tế trong vị trí của where T : M1 :-) Ví dụ cuối cùng tuyên bố rằng T phải thực hiện IHandle<MReset> (ngoài M1). Duh!

+0

[Liên quan/Nhân bản] (http://stackoverflow.com/questions/15316898/why-does-this-result-in-cs0695). Tôi không đóng như trùng lặp chỉ vì câu hỏi này "tại sao thứ tự khai báo lại tạo ra sự khác biệt như vậy?" không được trả lời ở đó. –

+0

Đó thực sự là liên kết có liên quan. Tôi vừa chỉnh sửa câu trả lời được chấp nhận ở đó để làm cho thông tin quan trọng từ thông số C# hiển thị rõ hơn: ** "Khai báo hạn chế không được xem xét khi xác định tất cả các loại có thể có." ** –

Trả lời

1

Sự cố được giải quyết - Tôi đã tìm thấy sự khác biệt tinh tế. Khi thứ tự của tờ khai được hoán đổi tôi nên không di chuyển where T : M1 kể từ khi IHandle<MReset> chế sau đó kết thúc lên được áp dụng cho T thay vì khai báo lớp:

class HandlerBase<T> : 
    IHandle<T> where T : M1, 
    IHandle<MReset> 
{ 
    ... same as before .. 
} 

Các đúng tái đặt hàng cần phải có được:

class HandlerBase<T> : 
    IHandle<T>, 
    IHandle<MReset> 
    where T : M1 
{ 
    ... same as before .. 
} 
1

@Siram chỉ ra rằng vấn đề độc đáo (nhưng không phải là khía cạnh thứ tự) đã được trả lời trong Why does this result in CS0695?:

Các spec ngôn ngữ C# (https://www.microsoft.com/en-us/download/confirmation.aspx?id=7029) thảo luận về "Tính độc đáo của giao diện thực hiện" trong 13.4.2 .: "Các giao diện được thực hiện bởi một khai báo kiểu chung phải duy trì duy nhất cho tất cả các kiểu được xây dựng có thể." Và sau đó, khi mô tả chi tiết của tờ séc: "Khai báo ràng buộc không được coi là khi xác định tất cả các loại được xây dựng có thể."

Vì sao tôi không chắc chắn; có lẽ người ta có thể xây dựng các ràng buộc lồng nhau hoặc chuỗi mà làm cho nó không thể cho trình biên dịch để chứng minh tính độc đáo, hoặc không phải tất cả các ràng buộc có thể được truyền thông qua các hội đồng (mà tôi nghĩ, sẽ là cần thiết cho quy tắc ngôn ngữ chung).

+0

Cảm ơn bạn đã đánh dấu "Tuyên bố ràng buộc không xem xét khi xác định tất cả các loại xây dựng có thể có. " - giải thích một phần của nó. Tôi mặc dù vẫn còn bối rối nhất về khía cạnh thứ tự (như tôi có thể thấy cả bạn và Siram đều biết). –

+0

Dường như trình biên dịch * không * xem xét các khai báo ràng buộc theo một cách bất ngờ nào đó. Nếu tôi loại bỏ 'trong đó T: M1' thì thứ tự khai báo trở nên không đáng kể và cả hai chuỗi kết quả trong CS0695" ... vì chúng có thể hợp nhất cho một số thay thế tham số kiểu "- như tôi mong đợi. –

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