Вернуться к основам - Ошибка компилятора C # - PullRequest
6 голосов
/ 21 января 2010
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 ]

15 голосов
/ 21 января 2010

Эрик Липперт только что написал в этой теме .

Основная суть заключается в том, чтобы гарантировать, что класс может "доверять" вызывающему объекту защищенного метода. Классы, которые имеют общий базовый класс - даже если эта общая база определяет защищенный метод - в этом отношении по сути незнакомы.

Пример Эрика основан на идее банковского приложения. Вместо того, чтобы воссоздать его пример, я просто срыгну его здесь:

// 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, что выводит вас за пределы круга доверия. Компилятор не анализирует все вызовы присваивания (или потенциальные вызовы присваивания), чтобы определить, является ли он «безопасным».

2 голосов
/ 21 января 2010

Из спецификации C #:

Когда защищенный элемент экземпляра доступ вне текста программы класс, в котором он объявлен, и когда защищенный внутренний экземпляр доступ к участнику вне программы текст программы, в которой он находится заявлено, доступ необходим для проходить через экземпляр тип производного класса, в котором доступ происходит.

MSDN ссылка здесь .

1 голос
/ 21 января 2010

Это взято непосредственно из 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
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...