views:

105

answers:

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

In the above code (compiled on VS2005), I get the following Compile Time error -

Error 1 Cannot access protected member 'BaseClass.BaseMethod()' via a qualifier of type 'BaseClass'; the qualifier must be of type 'DerivedClass' (or derived from it)

Can someone explain this behavior? Something is going fundamentally wrong here!

+15  A: 

Eric Lippert just blogged on this very topic.

The basic gist of it is to ensure that a class can "trust" the caller of a protected method. Classes that share a common base class--even if that common base defines the protected method--are essentially strangers in this regard.

Eric's example is based on the idea of a bank application. Rather than recreating his example, I'll just regurgitate it here:

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

While what you present seems like a no-brainer, if it were allowed to happen then the sort of shenanigans you see here would be possible. Right now you know that a protected method call either comes from your type (which you have control over) or from a class that you directly inherit from (which you know at the time that you compile). If it were opened to anyone that inherited from the declaring type, then you would never have the surety of knowing the types that can call your protected method.

While you're initializing your BaseClass variable to an instance of your own class, the compiler only sees that the variable is of type BaseClass, putting you outside of the circle of trust. The compiler doesn't analyze all of the assignment calls (or potential assignment calls) to determine if it's "safe".

Adam Robinson
Thanks Adam :) That was really helpful.
DotNetGuy
@DotNetGuy: Thanks; if this answers your question, please remember to mark it as your accepted answer so that others can more easily find the answer.
Adam Robinson
+2  A: 

From the C# spec:

When a protected instance member is accessed outside the program text of the class in which it is declared, and when a protected internal instance member is accessed outside the program text of the program in which it is declared, the access is required to take place through an instance of the derived class type in which the access occurs.

MSDN link here.

danben
+1  A: 

This is taken directly from MSDN: http://msdn.microsoft.com/en-us/library/bcd5672a%28VS.71%29.aspx

A protected member of a base class is accessible in a derived class only if the access takes place through the derived class type. For example, consider the following code segment:

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