2017-10-03 74 views
13
interface IBase 
{ 
    string Name { get; } 
} 

class Base : IBase 
{ 
    public Base() => this.Name = "Base"; 
    public string Name { get; } 
} 

class Derived : Base//, IBase 
{ 
    public Derived() => this.Name = "Derived"; 
    public new string Name { get; } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     IBase o = new Derived(); 
     Console.WriteLine(o.Name); 
    } 
} 

Trong trường hợp này, đầu ra sẽ là "Cơ sở".Đánh dấu rõ ràng lớp dẫn xuất khi triển khai giao diện của lớp cơ sở

Nếu tôi nói rõ rằng cụ nguồn gốc Ibase (đó là trên thực tế đã được thực hiện bởi lớp cơ sở Base và chú thích như vậy dường như là vô ích) đầu ra sẽ là "nguồn gốc"

class Derived : Base, IBase 
{ 
    public Derived() => this.Name = "Derived"; 
    public new string Name { get; } 
} 

lý do đó là gì hành vi?

VS 15.3.5, C# 7

+2

Tại sao nó hoạt động theo cách khác? Kỳ vọng của bạn là gì? –

+0

Kỳ vọng - cùng một đầu ra, cùng một thành viên được truy cập. Tôi không thấy lý do tại sao thêm giao diện để định nghĩa lớp khi giao diện như vậy đã được thực hiện bởi lớp cơ sở có thể thay đổi mọi thứ. – yevgenijz

+1

Bạn có hiểu 'chuỗi tên công cộng mới {get; } 'đang làm' Tên' trên 'Cơ sở'? –

Trả lời

13

Nó giải thích trong phần 13.4.4 đến 13.4.6 của đặc tả C# 5. Các phần có liên quan được trích dẫn bên dưới, nhưng về cơ bản nếu bạn nói rõ rằng một lớp thực hiện một giao diện, sẽ kích hoạt ánh xạ giao diện một lần nữa, do đó trình biên dịch sẽ lấy lớp làm lớp để sử dụng. .

13.4.4 mapping Interface

Một lớp học hoặc struct phải cung cấp triển khai của tất cả các thành viên của giao diện được liệt kê trong danh sách lớp cơ sở của lớp hoặc struct. Quá trình xác định vị trí của các thành viên giao diện trong một lớp thực hiện hoặc cấu trúc được gọi là ánh xạ giao diện.

Ánh xạ giao diện cho một lớp hoặc cấu trúc C xác định vị trí cho mỗi thành viên của mỗi giao diện được chỉ định trong danh sách lớp cơ sở là C. Việc thực hiện một thành viên giao diện cụ thể I.M, trong đó I là giao diện trong đó thành viên M được khai báo, được xác định bằng cách kiểm tra từng lớp hoặc cấu trúc S, bắt đầu bằng C và lặp lại cho mỗi lớp cơ sở liên tiếp C. :

  • Nếu S chứa một tuyên bố một giao diện thực hiện thành viên rõ ràng phù hợp với IM, sau đó thành viên này là việc thực hiện I.M.
  • Nếu không, nếu S chứa tuyên bố của thành viên công cộng không tĩnh khớp với M, thì thành viên này sẽ triển khai I.M. Nếu có nhiều hơn một thành viên phù hợp, không xác định thành viên nào đang triển khai I.M. Tình huống này chỉ có thể xảy ra nếu S là kiểu được xây dựng trong đó hai thành viên được khai báo trong kiểu chung có các chữ ký khác nhau, nhưng các đối số kiểu làm cho chữ ký của chúng giống hệt nhau.

...

13.4.5 Giao diện thực hiện quyền thừa kế

Một lớp kế thừa mọi cài đặt giao diện được cung cấp bởi các lớp cơ sở của nó. Nếu không thực hiện lại một cách rõ ràng một giao diện, một lớp dẫn xuất không thể thay đổi ánh xạ giao diện mà nó kế thừa từ các lớp cơ sở của nó.Ví dụ, trong tờ khai

interface IControl 
{ 
    void Paint(); 
} 
class Control: IControl 
{ 
    public void Paint() {...} 
} 
class TextBox: Control 
{ 
    new public void Paint() {...} 
} 

các Paint phương pháp trong TextBox ẩn phương pháp Paint trong Control, nhưng nó không làm thay đổi bản đồ của Control.Paint vào IControl.Paint và gọi Paint qua trường lớp và các trường hợp giao diện sẽ có tác dụng sau

Control c = new Control(); 
TextBox t = new TextBox(); 
IControl ic = c; 
IControl it = t; 
c.Paint();   // invokes Control.Paint(); 
t.Paint();   // invokes TextBox.Paint(); 
ic.Paint();   // invokes Control.Paint(); 
it.Paint();   // invokes Control.Paint(); 

...

13.4.6 Thực hiện lại giao diện

Một lớp thừa kế thực hiện giao diện được phép thực hiện lại giao diện bằng cách đưa nó vào danh sách lớp cơ sở.

Việc triển khai lại giao diện tuân theo chính xác các quy tắc ánh xạ giao diện giống như triển khai ban đầu của giao diện. Do đó, ánh xạ giao diện kế thừa không có tác dụng gì trên bản đồ giao diện được thiết lập để thực hiện lại giao diện. Ví dụ, trong tờ khai

interface IControl 
{ 
    void Paint(); 
} 
class Control: IControl 
{ 
    void IControl.Paint() {...} 
} 
class MyControl: Control, IControl 
{ 
    public void Paint() {} 
} 

thực tế là Control maps IControl.Paint vào Control.IControl.Paint không ảnh hưởng đến việc tái triển khai trong MyControl, mà bản đồ IControl.Paint vào MyControl.Paint.

+0

Phần giới thiệu của bạn giải thích nhiều hơn các trích dẫn. Nhưng một câu hỏi, khi bạn nói 'điều đó gây nên ánh xạ giao diện một lần nữa', nghĩa là 'một lần nữa' nghĩa là gì? Theo thứ tự nào là giao tiếp được thực hiện, không phải từ con đến cha mẹ, giống như thứ tự của các nhà xây dựng bị sa thải? Có nghĩa là bản đồ giao diện đầu tiên chỉ là 'thắng'? –

+0

Cảm ơn câu trả lời mở rộng như vậy. Điểm khó hiểu là có thể tái triển khai giao diện mà không thực hiện giao diện một cách rõ ràng (theo cú pháp) trong bất kỳ hai lớp nào (Base, Derived), chỉ bằng cách trộn lẫn trong giao diện với định nghĩa lớp. – yevgenijz

+0

@ nl-x: Tôi có nghĩa là ánh xạ giao diện được thực hiện khi biên dịch 'Base', và sau đó một lần nữa khi biên dịch' Derived'. Đây là lựa chọn biên dịch, vì vậy nó không có thứ tự thời gian thực hiện ...Tôi không chắc bạn đang hỏi gì. –

1

Nếu Derived không thực hiện IBase và tuyên bố new string Name, đó có nghĩa là Derived.NameIBase.Name là một cách logic không giống nhau. Vì vậy, khi bạn truy cập IBase.Name, hãy tìm kiếm trong lớp Base, triển khai IBase. Nếu bạn xóa thuộc tính new string Name, đầu ra sẽ là Derived, bởi vì bây giờ Derived.Name = Base.Name = IBase.Name. Nếu bạn triển khai IBase một cách rõ ràng, đầu ra sẽ là Derived, bởi vì bây giờ Derived.Name = IBase.Name. Nếu bạn truyền o đến Derived, đầu ra sẽ là Derived, bởi vì bây giờ bạn đang truy cập Derived.Name thay vì IBase.Name.

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