The private and the protected access modifiers are especially important in connection with inheritance. This section looks closer at these modifiers, along with the ability to declare a class member protected, internal, or internal protected.
As mentioned in the analysis of Listing 16.3, a protected class member can be accessed only from within its own class (like a private member) and from its subclasses. You should avoid declaring data members to be protected because it breaks with the encapsulation principles discussed in Chapter 3, "A Guided Tour Through C#: Part I." The next section looks more closely at this issue.
The Car class presented in Figure 16.7 is slightly different from the Car class presented in Listing 16.3, in that it does not contain the protected property Odometer to provide access to its private instance variable odometer. Consequently, odometer has to be accessed by referring to its own name. This is fine as long as we are accessing odometer from inside the Car class, as is done in its MoveForward method (see Figure 16.7). However, a private class member cannot be referenced by its name outside the class where it is declared. So odometer cannot be accessed from any derived classes, such as RacingCar, even though odometer is one of RacingCar's inherited class members.
Observe that if we had not overridden the MoveForward method of the Car class in the RacingCar class, MoveForward would have been passed on to the RacingCar class containing the original implementation specified in the Car class. This would allow us to call MoveForward for an object of the RacingCar class because this inherited method, even though now called through RacingCar, was originally defined in the Car class.
Our discussion in this section does not change the fact that all instance variables should be declared private. So the correct way to provide access to a private instance variable of a base class from a derived class is through a property or method that is defined in the base class and declared either protected (such as the Odometer property in Listing 16.3), internal, or public.
Why do we need to insist that all instance variables should be declared private and not protected so they are not even accessible in subclasses? After all, a RacingCar is a Car; so it should have as much right to access an odometer as a Car. Why can't we at least make instance variables accessible to subclasses with the protected keyword? The problem is this: If we declare an instance variable of a base class protected, anybody who wants to gain access to this instance variable merely needs to derive a class from this base class and is immediately granted access. The protected variable then becomes highly accessible for anybody who is willing to put in the extra effort.
Do Not Declare Any Function That Could Be of Value to a Subclass as private
Even though private instance variables are only accessible through a function member defined in the base class, private instance variables are, as we have seen earlier, still inherited from the base class. Similarly, private base class function members (except for constructors and destructors) are also inherited from the base class and can only be accessed from function member definitions that were written in the base class and then inherited by the derived class. It is impossible to write new or overriding function members in the derived class that call the private methods (properties and indexers) inherited from the base class.
So, functions that you think could be of value to a derived class should not be declared private; they should be made available with one of the other access modifiers.
Recall the internal access modifier presented in Chapter 15, "Namespaces, Compilation Units, and Assemblies." It grants access to all classes within the same assembly. It is possible to combine internal with the protected access modifier to form the internal protected access modifier, which is demonstrated in the following line:
internal protected int myNumber;
myNumber is now accessible from
Within the class where it is declared
Within a class that has been derived from the class where myNumber is declared
Within a class that belongs to the same assembly as myNumber's class
Briefly stated, internal protected provides internal or protected access.
The internal protected access modifier does not provide internal access AND protected access, (meaning access only from within its own class and from within a derived class that belongs to the same assembly). You can, however, implement this by declaring your class internal and the class member protected.
Presenting the internal protected access modifier concludes our journey through the access modifiers found in C#. Table 16.1 provides a brief overview of C#'s access modifiers.
|private||A private type member is only accessible from within the type where it is defined. Class and struct members are private by default.|
|public||A public type or type member is accessible from any part of the program.|
|internal||An internal type or type member is accessible only from within the assembly where it is defined.|
|protected||A protected class member is accessible only from within the class where it is defined or from within a class that is derived from that class.|
|internal protected||An internal protected class member is accessible only from within the class where it is defined or from within a class that is derived from that class or from within the assembly where it is defined.|