2012-09-09 26 views
8

Nếu tôi thử một diễn viên không hợp lệ từ một lớp đến một giao diện, thì trình biên dịch không phàn nàn (lỗi xảy ra khi chạy); Tuy nhiên, nó không có khiếu nại, nếu tôi thử một diễn viên tương tự với một lớp trừu tượng.Tại sao không có lỗi trình biên dịch khi tôi truyền một lớp tới giao diện mà nó không thực hiện?

class Program 
{ 
    abstract class aBaz 
    { 
     public abstract int A { get; } 
    } 

    interface IBar 
    { 
     int B { get; } 
    } 

    class Foo 
    { 
     public int C { get; } 
    } 

    static void Main() 
    { 
     Foo foo = new Foo(); 

     // compiler error, as expected, since Foo doesn't inherit aBaz 
     aBaz baz = (aBaz)foo; 

     // no compiler error, even though Foo doesn't implement IBar 
     IBar bar = (IBar)foo; 
    } 
} 

Tại sao không trình biên dịch từ chối các diễn viên từ Foo-Ibar, khi nó (có vẻ?) Không hợp lệ? Hoặc, để lật câu hỏi, nếu trình biên dịch cho phép diễn viên "không hợp lệ" này giao diện IBar, tại sao nó không cho phép diễn viên "không hợp lệ" tương tự vào lớp trừu tượng aBaz?

+1

http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx – SLaks

+0

Điều này truyền đến một giao diện bit tôi trong mông tối nay ... và cậu bé đã làm đau. –

+1

Bạn không đánh dấu 'Foo' là' niêm phong'. Vì vậy, các diễn viên rõ ràng sẽ biên dịch. Có thể tồn tại một lớp kế thừa từ 'Foo' được bổ sung thêm' IBar'. Nếu bạn sử dụng 'lớp Foo' bị đóng kín, thì phép đúc thành' IBar' sẽ là bất hợp pháp tại thời gian biên dịch. (Trong trường hợp này, lớp 'Foo' được lồng riêng, vì vậy ngay cả khi diễn viên phải được cho phép, có thể hiểu rằng trình biên dịch C# có thể đưa ra một _warning_ (không phải lỗi kể từ khi thông số nói rõ ràng tồn tại) vì nó có thể thấy phạm vi đầy đủ của lớp lồng nhau. Nhưng đó có thể là trường hợp hiếm hoi.) _Edit: _ Bây giờ tôi thấy điều này được Alexei bảo hiểm bên dưới. –

Trả lời

7

Bạn cần hiểu hệ thống kế thừa của .Net để xem lý do tại sao điều này có ý nghĩa. Trong .Net, một lớp có thể kế thừa từ chỉ một lớp cơ sở nhưng có thể thực hiện bất kỳ số lượng giao diện nào.

class Program 
{ 
    abstract class aBaz 
    { 
     public abstract int A { get; } 
    } 

    interface IBar 
    { 
     int B { get; } 
    } 

    class Foo 
    { 
     public int C { get; } 
    } 

    class BarableFoo : Foo, IBar 
    { 
     public int C { get; } 
    } 

    static void Main() 
    { 
     // This is why the compiler doesn't error on the later cast 
     Foo foo = new BarableFoo(); 

     // compiler error: aBaz is a class and the compiler knows that 
     // Foo is not a _subclass_ of aBaz. 
     aBaz baz = (aBaz)foo; 

     // no compiler error: the class Foo does not implement IBar, however at runtime 
     // this instance, "foo", might be a subclass of Foo that _implements_ IBar. 
     // This is perfectly valid, and succeeds at runtime. 
     IBar bar = (IBar)foo; 

     // On the other hand... 
     foo = new Foo(); 

     // This fails at runtime as expected. 
     bar = (IBar)foo; 
    } 

} 

Trong ví dụ ban đầu cực kỳ đơn giản trong câu hỏi, nó có vẻ như trình biên dịch có thể phát hiện trường hợp này của foo là không bao giờ được bột nhôm để Ibar, nhưng đó là nhiều hơn một "tốt đẹp để có" cảnh báo hơn là vấn đề chính xác về ngôn ngữ.

+1

+1. Ngoài ra thêm câu trả lời cho thấy trường hợp khi cast không thành công tại thời gian biên dịch. –

+0

Tôi đã mong đợi rằng nếu bạn muốn truyền một Foo đến một quán Bar, trước tiên bạn sẽ phải đưa nó vào BarableFoo, để nó làm rõ những loại bạn mong đợi – Andy

+0

@Andy - Bạn đang nói về dòng cuối cùng hay trung '(IBar) foo'? – BitwiseMan

6

Toàn bộ điểm của diễn viên là để loại bỏ lỗi trình biên dịch đó.
(ví dụ, nếu bạn biết rằng foo thực sự là một thể hiện của một subtype mà thực hiện giao diện)

Nếu trình biên dịch có thể chứng minh rằng nó là không thể đối với các diễn viên để thành công, nó sẽ vẫn đưa ra một lỗi. (ví dụ: nếu bạn truyền tới một lớp học không thuộc hệ thống phân cấp của nó)

+0

Ok ... vì vậy bạn đang nói trình biên dịch có thể chứng minh dàn diễn viên đầu tiên không hợp lệ, nhưng không phải là lần thứ hai. Nhưng tại sao nó không thể chứng minh được thứ hai? Không Foo (hoặc một trong những tổ tiên của nó) phải được tuyên bố là thực hiện IBar để làm cho rằng diễn viên có thể? – McGarnagle

+1

@dbaseman: Nếu tôi viết 'class Baz: Foo, IBar' thì sao? – SLaks

+1

@McGarnagle: Một trong những hậu duệ của Foo sẽ phải thực hiện 'IBar' để làm cho diễn viên có thể, nhưng trong nhiều trường hợp không có cách nào để trình biên dịch biết liệu một hội đồng có chứa một số hậu duệ của' Foo' thực hiện ' IBar' có thể được tải. – supercat

5

Và để hiển thị trình biên dịch đó không phải là ngu ngốc có trường hợp actulaly khi cast sẽ thất bại tại thời gian biên dịch: nếu trình biên dịch có thể chứng minh rằng không có các lớp khác có thể lấy được từ lớp nó sẽ thất bại đúc để giao diện tại thời gian biên dịch:

sealed class NoBar 
{ 
} 

struct NoBarValue 
{ 
} 

IBar noBar = (IBar)(new NoBar()); // fails at compile time 
IBar noBarValue = (IBar)(new NoBarValue()); // fails at compile time 

Trong trường hợp đầu tiên (NoBar) lớp được niêm phong một cách rõ ràng (và do đó không có lớp dẫn xuất có thể thực hiện IFoo) và trình biên dịch biết rằng nó không tự thực hiện IBar - do đó có thể không thành công tại thời gian biên dịch. Trường hợp thứ hai (NoBarValue) là tương tự với sự khác biệt duy nhất mà các loại giá trị (struct) được niêm phong hoàn toàn.

+0

Ngoài ra, cảm ơn bạn. – McGarnagle

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