回归基础 - C#编译器错误

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

在上述代码(在VS2005上编译)中,我得到了以下的编译时错误 -
“错误1 无法通过类型为“BaseClass”的限定符访问受保护的成员'BaseMethod()'; 限定符必须是“DerivedClass”类型(或派生自其)”
有人能解释一下这种行为吗?这里似乎出现了根本性的问题!
3个回答

15

Eric Lippert刚刚在他的博客上谈到了这个话题

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

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类型,这使你处于信任圈之外。编译器不会分析所有赋值调用(或潜在的赋值调用)来确定它是否“安全”。

@DotNetGuy:谢谢;如果这个回答解决了你的问题,请记得将其标记为已接受的答案,以便其他人更容易地找到答案。 - Adam Robinson

2
从C#规范中:

当在声明它的类的程序文本之外访问受保护的实例成员,以及在声明它的程序的程序文本之外访问受保护的内部实例成员时,要求通过派生类类型的实例进行访问。

MSDN链接 这里


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接