2010-01-21 36 views
6
public class BaseClass 
{ 
    protected void BaseMethod() 
    { 

    } 
} 

public class DerivedClass : BaseClass 
{ 
    public void Test() 
    { 
    DerivedClass d1 = new DerivedClass(); 
    d1.BaseMethod(); // No error here.   

    BaseClass b1 = new DerivedClass(); 
    b1.BaseMethod(); // I get compile-time error for this. Why ? 
    } 
} 

Trong đoạn mã trên (biên soạn trên VS2005), tôi nhận được lỗi Compile Time sau -Về cơ bản - C# Compiler Lỗi

Lỗi 1 Không thể truy cập thành viên được bảo vệ 'BaseClass.BaseMethod() 'thông qua vòng loại loại' BaseClass '; các vòng loại phải loại 'DerivedClass' (hoặc có nguồn gốc từ nó)

Ai đó có thể giải thích hành vi này? Cái gì đó về cơ bản là sai ở đây!

Trả lời

15

Eric Lippert chỉ blogged on this very topic.

Ý chính cơ bản của nó là để đảm bảo rằng một lớp có thể "tin tưởng" người gọi của một phương pháp được bảo vệ. Các lớp chia sẻ một lớp cơ sở chung - ngay cả khi cơ sở chung đó định nghĩa phương thức được bảo vệ - về cơ bản là những người lạ trong vấn đề này.

Ví dụ của Eric dựa trên ý tưởng về đơn đăng ký ngân hàng. Thay vì tái tạo gương sáng của mình, tôi chỉ sẽ nôn ra nó ở đây:

// Good.dll: 

public abstract class BankAccount 
{ 
    abstract protected void DoTransfer(
    BankAccount destinationAccount, 
    User authorizedUser, 
    decimal amount); 
} 
public abstract class SecureBankAccount : BankAccount 
{ 
    protected readonly int accountNumber; 
    public SecureBankAccount(int accountNumber) 
    { 
    this.accountNumber = accountNumber; 
    } 
    public void Transfer(
    BankAccount destinationAccount, 
    User authorizedUser, 
    decimal amount) 
    { 
    if (!Authorized(user, accountNumber)) throw something; 
    this.DoTransfer(destinationAccount, user, amount); 
    } 
} 
public sealed class SwissBankAccount : SecureBankAccount 
{ 
    public SwissBankAccount(int accountNumber) : base(accountNumber) {} 
    override protected void DoTransfer(
    BankAccount destinationAccount, 
    User authorizedUser, 
    decimal amount) 
    { 
    // Code to transfer money from a Swiss bank account here. 
    // This code can assume that authorizedUser is authorized. 
    // We are guaranteed this because SwissBankAccount is sealed, and 
    // all callers must go through public version of Transfer from base 
    // class SecureBankAccount. 
    } 
} 
// Evil.exe: 
class HostileBankAccount : BankAccount 
{ 
    override protected void Transfer(
    BankAccount destinationAccount, 
    User authorizedUser, 
    decimal amount) { } 
    public static void Main() 
    { 
    User drEvil = new User("Dr. Evil"); 
    BankAccount yours = new SwissBankAccount(1234567); 
    BankAccount mine = new SwissBankAccount(66666666); 
    yours.DoTransfer(mine, drEvil, 1000000.00m); // compilation error 
    // You don't have the right to access the protected member of 
    // SwissBankAccount just because you are in a 
    // type derived from BankAccount. 
    } 
} 

Trong khi những gì bạn trình bày có vẻ như không có trí tuệ, nếu nó được phép xảy ra sau đó các loại shenanigans bạn thấy ở đây sẽ có thể. Ngay bây giờ, bạn biết rằng cuộc gọi phương thức được bảo vệ đến từ loại của bạn (mà bạn có quyền kiểm soát) hoặc từ một lớp mà bạn trực tiếp kế thừa từ (mà bạn biết tại thời điểm bạn biên dịch). Nếu nó được mở cho bất kỳ ai thừa hưởng từ kiểu khai báo, thì bạn sẽ không bao giờ có sự bảo đảm khi biết các kiểu có thể gọi phương thức được bảo vệ của bạn.

Trong khi bạn đang khởi tạo biến số BaseClass cho một phiên bản của lớp của riêng bạn, trình biên dịch chỉ thấy biến đó thuộc loại BaseClass, đưa bạn ra khỏi vòng tròn tin cậy. Trình biên dịch không phân tích tất cả các cuộc gọi chuyển nhượng (hoặc các cuộc gọi chuyển nhượng tiềm năng) để xác định xem nó có "an toàn" hay không.

+0

Cảm ơn Adam :) Điều đó thực sự hữu ích. – DotNetGuy

+0

@DotNetGuy: Cảm ơn; nếu điều này trả lời câu hỏi của bạn, hãy nhớ đánh dấu câu hỏi đó là câu trả lời được chấp nhận của bạn để người khác có thể dễ dàng tìm thấy câu trả lời. –

2

Từ C# spec:

Khi một thành viên dụ bảo vệ được truy cập bên ngoài các văn bản chương trình của lớp trong đó nó được công bố, và khi một nội thành viên dụ bảo vệ được truy cập bên ngoài chương trình văn bản của chương trình được tuyên bố là , yêu cầu truy cập vào thông qua một phiên bản loại có nguồn gốc mà truy cập xảy ra.

Liên kết MSDN here.

1

này được lấy trực tiếp từ MSDN: http://msdn.microsoft.com/en-us/library/bcd5672a%28VS.71%29.aspx

Một thành viên bảo vệ của một lớp cơ sở có thể truy cập trong một lớp học có nguồn gốc chỉ khi truy cập diễn ra thông qua các loại hình lớp dẫn xuất. Ví dụ: hãy xem xét đoạn mã sau:

class A 
{ 
    protected int x = 123; 
} 

class B : A 
{ 
    void F() 
    { 
     A a = new A(); 
     B b = new B(); 
     a.x = 10; // Error 
     b.x = 10; // OK 
    } 
} 
Các vấn đề liên quan