Эрик Липперт только что написал в этой теме .
Основная суть заключается в том, чтобы гарантировать, что класс может "доверять" вызывающему объекту защищенного метода. Классы, которые имеют общий базовый класс - даже если эта общая база определяет защищенный метод - в этом отношении по сути незнакомы.
Пример Эрика основан на идее банковского приложения. Вместо того, чтобы воссоздать его пример, я просто срыгну его здесь:
// 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
, что выводит вас за пределы круга доверия. Компилятор не анализирует все вызовы присваивания (или потенциальные вызовы присваивания), чтобы определить, является ли он «безопасным».