返回基础知识 - C#编译器错误

时间:2010-01-21 16:20:23

标签: c# compiler-errors

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 ? 
  }
}

在上面的代码中(在VS2005上编译),我得到以下编译时错误 -

  

错误1无法访问受保护的成员   'BaseClass.BaseMethod()'通过   'BaseClass'类型的限定符;该   限定符必须是类型   'DerivedClass'(或从中衍生出来)

有人可以解释这种行为吗?这里出现了根本性的错误!

3 个答案:

答案 0 :(得分:15)

Eric Lippert blogged on this very topic

它的基本要点是确保一个类可以“信任”受保护方法的调用者。共享一个公共基类的类 - 即使该公共基类定义了受保护的方法 - 在这方面基本上是陌生的。

Eric的例子是基于银行应用程序的想法。而不是重新创建他的例子,我只会在这里反刍:

// 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.
  }
}

虽然你提出的东西看起来很简单,如果它被允许发生,那么你在这里看到的那种恶作剧是可能的。现在,您知道受保护的方法调用来自您的类型(您可以控制)或来自您直接继承的类(您在编译时知道)。如果它对从继承声明类型继承的任何人开放,那么你将无法确定知道可以调用受保护方法的类型。

当您将BaseClass变量初始化为您自己类的实例时,编译器只会看到该变量的类型为BaseClass,从而使您置于信任圈之外。编译器不会分析所有赋值调用(或潜在的赋值调用)以确定它是否“安全”。

答案 1 :(得分:2)

来自C#规范:

  

受保护的实例成员是   在程序文本之外访问   声明它的类,和   当受保护的内部实例时   会员在该计划之外访问   它所在的程序的文本   声明,访问是必需的   通过一个实例发生   派生类访问的类型   发生。

MSDN链接here

答案 2 :(得分:1)

这是直接从MSDN获取的:http://msdn.microsoft.com/en-us/library/bcd5672a%28VS.71%29.aspx

只有通过派生类类型进行访问时,才能在派生类中访问基类的受保护成员。例如,请考虑以下代码段:

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
   }
}