This article provides an in-depth discussion of member and type visibility. It illustrates when and where to make a member either public or private, protected or internal. It even gives you tips to use when building your own libraries. Keep reading to find out more!
Contributed by Ayad Boudiab Rating: / 2 April 16, 2008
Before we get started, let’s clarify some terms. Types are the building blocks of an object-oriented language, like C#. An application is a collection of these types interacting together to provide useful information. When we refer to types, we mean classes, structures, interfaces, enumerations, and delegates (these types will be explored in more detail in upcoming articles).
A type, like a class, contains members. Example of members will be methods, fields, constructors, and so on…
As stated earlier, an object-oriented application is a collection of types, and a type is a collection of members. When designing a type, a common question arises: should the user of the class (another developer for instance) have access to a specific field in the class, or should that field be privately owned and manipulated by the type? In other words, what is the visibility of each member of the class? The reason this is important is that you do not want the user of your class to change the state of a certain field to an unacceptable value and thus, make that instance of the class useless.
Now that we've clarified the importance of member visibility, here is a summary of those visibilities before we explore them in more detail:
public
This marks the member as accessible from other classes.
private
This marks the member as accessible from only the class that defined the member. In C#, all members are private by default.
protected
This marks the member as accessible from the defining class, and any derived classes.
internal
This marks the member as accessible from other types in the same assembly.
protected internal
This marks the member as accessible from the current assembly or from types derived from the defining class in the current assembly.
Note: C# uses the term ‘access modifier’ to describe the accessibility.
The public keyword is an access modifier for types and type members. In this section, however, we are discussing public for type members only. There are no restrictions on accessing public members. Any type within or outside the assembly can access public members. In the following example, x and y are declared public in the Point class:
using System;
namespace PublicMembers
{
class Program
{
static void Main(string[] args)
{
Point p = new Point();
p.x = 4;
p.y = 7;
Console.WriteLine("The point p has the following coordinates: ("
+ p.x + "," + p.y + ")");
}
}
class Point
{
public float x;
public float y;
}
}
Declaring x and y public makes them visible to other classes (and even other assemblies). In the example listed above, the Main method in the Program class manipulated x and y directly (through p.x and p.y).
Declaring field members public is not a good programming practice. One of the key concepts in object-oriented programming is information hiding. The data part of a class should always be declared private (I'll discuss this shortly). You then provide public methods that interface with the user of the class to get (or set) the private data. You might ask: why do I need that extra layer when I can access the data directly? Well, because you do not need the user of the class to corrupt the data inside the class (intentionally or unintentionally). For instance, if we declare an age variable to be public, and the user of the class changed that value to -1, then the data is corrupted and the behavior of the program is unpredictable.
On the other hand, if we declare the age variable to be private, then the users of the class do not have direct access to the age member. They can call a method or a property to change the value of age. The difference in this case is that you can add logic to the method (or property) to reject any value that does not make business sense.
The private keyword is a member access modifier. Private members are accessible only within the body of the class in which they are declared. In order to access private members outside of the class, you need to provide public methods (or properties). Here is an example to illustrate the point:
class Program
{
static void Main(string[] args)
{
Book b = new Book();
b.YearPublished = 1929;
}
}
class Book
{
private string title;
private int year_published;
public string Title
{
get { return title; }
set { title = value; }
}
public int YearPublished
{
get { return year_published; }
set
{
if (value < 1930)
{
Console.WriteLine("year cannot be less then 1930n" +
"year is reset to 1930");
year_published = 1930;
}
else
year_published = value;
}
}
}
title and year_published are declared private members, which means they can only be accessed within the Book class. In order to give users of the class access to these members, two properties have been added to the class (Title and YearPublished). These properties have been declared public to get and set the values of title and year_published. Notice that we added a condition when setting the year the book was published. In this case, we are not accepting any books published before 1930. If you declared the data public, the user of the class (being another programmer) could have changed the year in any way he/she sees fit. That could have led to data corruption.
Two important items to keep in mind about properties:
They are very useful when getting and setting private data. Properties themselves can be declared private, protected, or public. That depends on how much flexibility you need to give the user of your class.
Properties are turned into getters and setters behind the scenes. Once you compile your code, use ildasm.exe to view the content of the assembly. You notice that the class properties are changed to gets and sets. So, if you added gets and sets in addition to the properties, the compiler will give you an error message (basically you are declaring a method twice!).
Another access modifier supported in C# is protected. A protected member is accessible within its class and by derived classes. Derived classes are classes that inherit the functionality of classes higher in the hierarchy. For example, a son inherits some of the characteristics of his father, a circle inherits some of the characteristics of a shape, and so on…
In the case of managers and employees, we know that a manager is, after all, an employee. So it makes sense to create data members inside the Employee class that only the Manager class can access (or any other class that inherits from Employee (like SalesPerson)). Here is an illustration:
class Program
{
static void Main(string[] args)
{
Manager m = new Manager("Tim", 12345, 1500);
m.PrintInfo();
}
}
class Employee
{
protected string name;
protected int id;
public Employee(string e_name, int e_id)
{
name = e_name;
id = e_id;
}
}
class Manager : Employee
{
private double bonus;
public Manager(string m_name, int m_id, double m_bonus)
: base(m_name, m_id)
{
bonus = m_bonus;
}
public void PrintInfo()
{
Console.WriteLine("Manager " + name +
" with id " + id + " has bonus " + bonus);
}
}
Notice that name and id in the Employee class were successfully accessed from the Manager class because they were declared protected in the Employee class. And Manager inherits from Employee (C# uses the semicolon (:) to illustrate inheritance). In the Manager constructor, we used the base keyword to access the Employee’s constructor in order to initialize name and id.
As far as internal and protected internal members go, they are mostly useful when you are creating libraries (managed dlls). internal defines members that are accessible by any type in the same assembly. protected internal defines a member whose access is limited to the current assembly or to types derived from the defining class in the current assembly. Here is an example that illustrates their use:
Console.WriteLine("I am a protected internal method");
}
}
After discussing member visibility, one question comes up: how about type visibility? Should the class be accessed by other classes outside the assembly, or should its accessibility only be limited to the defining assembly?
When it comes to type visibility, there are two access modifiers of concern: public or internal.
Creating a public type means that the type is accessed from other types in the current assembly as well as other assemblies. This is useful when you are creating a code library. Here is an example:
public class Book
{
private string title;
private string author;
private string ISBN;
…
}
Since the Book class is public, it can be accessed from other assemblies. The users of this assembly can create Book objects, inherit from the Book class, and so on…
Internal Type
Internal types, on the other hand, can be used only by the assembly in which they are defined. So, if we make the Book class internal, other assemblies cannot in any way interact with the Book class:
internal class Book
{
private string title;
private string author;
private string ISBN;
…
}
Please note that the default access modifier for a type is internal. In other words, if you do not specify the access modifier, it will be internal. So the following declarations are identical:
class Book {...}
internal class Book {...}
Conclusion
When creating types, it is very important to pay special attention to the type member’s visibility. Declaring a member private makes it accessible only to the defining class. Declaring the member public, on the other hand, makes it accessible by any type. When it comes to inheritance, try to keep members protected so that only subclasses can access them. As far as building your own libraries, internal and protected internal members come handy.