【问题标题】:Back to basics - C# Compiler Error回到基础 - C# 编译器错误
【发布时间】:2010-01-21 16:20:22
【问题描述】:
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'(或派生自它)

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

【问题讨论】:

    标签: c# compiler-errors


    【解决方案1】:

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

    【讨论】:

    • 感谢 Adam :) 这真的很有帮助。
    • @DotNetGuy:谢谢;如果这回答了您的问题,请记得将其标记为您接受的答案,以便其他人更容易找到答案。
    【解决方案2】:

    来自 C# 规范:

    当一个受保护的实例成员是 在程序文本之外访问 声明它的类,以及 当一个受保护的内部实例 成员在程序外被访问 它所在的程序的文本 声明,访问是必需的 通过一个实例发生 派生类类型在其中访问 发生。

    MSDN 链接here.

    【讨论】:

      【解决方案3】:

      这直接取自 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
         }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-08-12
        • 1970-01-01
        • 2010-11-28
        • 1970-01-01
        • 2011-08-19
        • 2013-12-30
        • 2013-02-26
        • 2014-01-29
        • 2015-09-11
        相关资源
        最近更新 更多