Search code examples
c#propertiesencapsulationsettergetter

Why a derived class can't access a protected getter from the base class?


I have a class like this:

public class Base
{
    public Base(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; }
    public string PrettyName
    {
        get { return Prettify(Name); }
    }
}

and I derive from it:

public class Derived : Base
{
    public Derived(Base b) : base(b.Name) { }
}

The Name property should not be accessed; the logical name should be accessed only by PrettyName. So I thought I'd make the property like this:

    public string Name { protected get; set; }

But I'm getting this:

Cannot access protected member 'Name' via a qualifier of type 'Base'; the qualifier must be of type 'Derived' (or derived from it)  

Why is that? The getter should be exposed to the base class and all its child classes. Am I getting something wrong here?


Solution

  • The getter should be exposed to the base class and all its child classes.

    No, not quite. This isn't a matter of automatically implemented properties - it's a matter of what protected means.

    Access to a protected member within a subclass has to be through an instance of that subclass (or a further subclass). You can't use Base.Name for an arbitrary Base in Derived.

    From section 3.5.3 of 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 must take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place through an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class.

    A simple solution would be to overload the constructor in Base:

    protected Base(Base b) : this(b.Name)
    {
    }
    

    then in Derived:

    public Derived(Base b) : base(b) { }
    

    At that point, you can make the Name setter private, too - or better yet, make it fully read-only:

    private readonly string name;
    public string Name { get { return name; } }