Access Specifiers in C#

Access Specifiers in C# with Examples

In this article, I am going to discuss the Access Specifiers in C# with Examples. Please read our previous article before proceeding to this article where we discussed the Destructor in C# with an example. As part of this article, we are going to discuss the following pointers with examples.

  1. What are Access Specifiers in C#?
  2. Types of Access Specifiers supported by dot net.
  3. Understanding Type and Type members in C#.
  4. Understand Private, Public, Protected, Internal, and ProtectedInternal access specifiers with examples.
What are Access Specifiers in C#?

The Access Specifiers in C# are also called access modifiers which are used to define the scope of the type as well as the scope of their members. That is who can access them and who cannot access them are defined by the Access Specifiers.

Types of Access Specifiers:

C# supports 5 access specifiers, they are as follows

  1. Private
  2. Internal
  3. Protected
  4. Protected Internal
  5. Public

Note: Members that are defined in a type with any scope or specifiers are always accessible within that type; restriction comes into picture only when they try to access them outside of the type.

Understand Type and Type members in C#:

In the example below Customer is the Type and private fields (_id, _firstName, _lastName), Properties (Id, FirstName, LastName) and method GetFullName() are type members.

namespace AccessSpecifierDemo
{
    public class Customer
    {
        #region Private Fields
        private int _id;
        private string _firstName;
        private string _lastName;
        #endregion

        #region Properties
        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }
        #endregion

        #region Methods
        public string GetFullName()
        {
            return this._firstName + " " + this._lastName;
        }
        #endregion
    }
}

So in general classes, structs, enums, interfaces, delegates are called types and fields, properties, constructors, methods, etc. that normally reside within a type are called type members. The Type members can have all the access modifiers whereas types can have only 2 (internal, public) of the 5 access modifiers

The customer class makes use of regions. Using regions we can expand and collapse sections of our code either manually, or using visual studio Edit -> Outlining -> Toggle All Outlining

Let’s discuss each access specifiers in C# with some examples.

Private and Public Access Specifiers in C#:

Private members are available only within the containing type whereas public members are available anywhere. There is no restriction for public members. Let understand Private and Public Members with an example:

namespace AccessSpecifierDemo
{
    public class Customer
    {
        private int _id;
        public int Id
        {
            get
            {
                return _id;
            }
            set
            {
                _id = value;
            }
        }
    }

    public class MainClass
    {
        private static void Main()
        {
            Customer CustomerInstance = new Customer();
            CustomerInstance.Id = 101;
            // Compiler Error: 'Customer._id' is inaccessible due to its protection level
            // CustomerInstance._id = 101;
        }
    }
}

In the above example, _id is private so this member is only available within the Customer class (Containing Type). It is a compile-time error to access _id outside of the Customer Class.

The following line in the MainClass will generate a compiler error stating, ‘Customer._id’ is inaccessible due to its protection level.

CustomerInstance._id = 101;

On the other hand, since Id is a public member, we can access this member anywhere even outside of the Customer class. In fact, we invoke the Id property of the Customer class in the Main() method as shown below.

CustomerInstance.Id = 101;

Protected Access Specifier in C#:

Protected Members are available within the containing type and to the types that are derived from the containing type. Let us understand this with an example.

namespace AccessSpecifierDemo
{
    public class Customer
    {
        protected int ID = 101;
        public void PrintID()
        {
            //Protected member ID is accessible with in Customer class
            Console.WriteLine(this.ID);
        }
    }
    public class CorporateCustomer : Customer
    {
        public void PrintCustomerID()
        {
            CorporateCustomer corporateCustomerInstance = new CorporateCustomer();
            // Can access the base class protected instance member using the derived class object
            Console.WriteLine(corporateCustomerInstance.ID);
            // Can access the base class protected instance member using this or base keyword
            Console.WriteLine(this.ID);
            Console.WriteLine(base.ID);
        }
    }
    public class RetailCustomer
    {
        public void PrintCustomerID()
        {
            RetailCustomer retailCustomerInstance = new RetailCustomer();
            //RetailCustomer class is not deriving from Customer class, hence it is an error
            //to access Customer class protected ID member, using the retailCustomerInstance
            //Console.WriteLine(retailCustomerInstance.ID); //Error
            //Both these below lines also produce the same Error
            //Console.WriteLine(this.ID); // Error
            //Console.WriteLine(base.ID); // Error
        }
    }
}

The customer class defines a protected member IDCorporateCustomer class derives from the Customer class so protected ID member is accessible in the Customer class (Containing Type) and also from the CorporateCustomer class (Derived Type). 

Within the PrintID() method in the Customer class Protected member ID is accessible.

Console.WriteLine(this.ID);

There are 3 ways to access the base class protected member in the derived class as shown below.

Using the derived class object: Console.WriteLine(corporateCustomerInstance.ID);
Using this keyword: Console.WriteLine(this.ID);
Using the base keyword: Console.WriteLine(base.ID);

On the other hand, the RetailCustomer class is not deriving from the Customer class hence it’s a compile-time error to access Customer class protected ID member.

Internal Access Specifier in C#:

Whenever a member is declared with an internal access modifier then it is available anywhere within the containing assembly. It’s a compile-time error to access an internal member from outside the containing assembly.

To understand the internal access modifier we need 2 assemblies. To generate the 2 assemblies we need to follow these steps.

  1. Open Solution Explorer (From the View menu, select Solution Explorer)
  2. From the Solution Explorer, right-click on the project and select Add -> New Project

In the Add New Project Dialog Box, Select Visual C# from the Installed Templates section, and select Class Library from the center pane, and then enter AssemblyOne for the Name of the project and click OK.

  1. Follow steps 2 & 3, to create the AssemblyTwo project.
  2. If you have followed these steps correctly, we should now see three projects in the solution explorer.

Now if we build the solution we should have 3 assemblies generated. Two dll’s and one exe. To locate the physical assembly follow these steps.

  1. Right-click on the AssemblyOne project, in solution explorer and select Open Folder in Windows Explorer.
  2. Open bin folder
  3. Now open Debug folder
  4. In the Debug folder, you should see AssemblyOne.dll, which is the physical assembly.

Copy and paste the following code:

namespace AssemblyOne
{
    public class AssemblyOneClassI
    {
        internal int ID = 999;
    }
    public class AssemblyOneClassII
    {
        public void Test()
        {
            AssemblyOneClassI instance = new AssemblyOneClassI();
            // Can access inetrnal member ID, AssemblyOneClassII and AssemblyOneClassI
            // are present in the same assembly            
            Console.WriteLine(instance.ID);
        }
    }
}

In this example, AssemblyOneClassI has an internal member ID. We can access ID member from AssemblyOneClassII because this class is also present in the same assembly as AssemblyOneClassI.

Now Copy and Paste the following code in Class1.cs of AssemblyTwo project.

using AssemblyOne;
namespace AssemblyTwo
{
    public class AssemblyTwoClassI
    {
        public void Test()
        {
            AssemblyOneClassI instance = new AssemblyOneClassI();
            //Console.WriteLine(instance.ID);        
        }
    }
}
Note: We will get 3 compiler errors at this point. To solve this we need to add an assembly reference. Follow these steps.
  1. Expand the References folder under the AssemblyTwo project, from Solution Explorer.
  2. Right-click on References folder and select Add Reference
  3. From the Add Reference dialog box, select the Projects tab
  4. From the list, select the AssemblyOne project and click OK.

At, this point all the compiler errors should have gone.

Uncomment the following line from the Class1.cs file from the AssemblyTwo project and rebuild the solution.
Console.WriteLine(instance.ID);

Now, we will get a compiler error stating 

Access Specifiers in C# with Examples

This is because AssemblyTwoClassI is not present in AssemblyOne assembly and hence cannot access the internal ID member defined in AssemblyOne assembly. This proves that internal members are only accessible within the same assembly. Code outside of the containing assembly cannot access internal members.

Protected Internal Access Specifier:

Protected Internal members can be accessed anywhere within the assembly in which it is declared or from within a derived class in another assembly. It is a combination of protected and internal. If you understood the protected and internal access specifiers in C#, then this should be very easy to follow.

Now change the access modifier from internal to protected internal for ID member in AssemblyOneClassI of class1.cs file in the AssemblyOne project.

internal int ID = 999; to protected internal int ID = 999;

Finally, modify the code in Class1.cs file in the AssemblyTwo project as shown below.

using AssemblyOne;
namespace AssemblyTwo
{
    // Make AssemblyTwoClassI inherit from AssemblyOneClassI    
    public class AssemblyTwoClassI : AssemblyOneClassI
    {
        public void Test()
        {
            AssemblyOneClassI instance = new AssemblyOneClassI();
            // Access the base class member using the base keyword            
            Console.WriteLine(base.ID);
        }
    }
}

So this shows protected internal ID member defined in AssemblyOne is accessible in AssemblyTwo.

As of now, we have discussed how to use access specifiers with the type members. Let see how to use the access specifiers in C# with the type.

Access Specifiers with Type:

We can use all the 5 access modifiers with type members but type allows only internal and public access modifiers. It is a compile-time error to use private, protected and protected internal access modifiers with types. 

The following code will generate a compiler error stating Elements defined in a namespace cannot be explicitly declared as private, protected, or protected internal

namespace AccessSpecifierDemo
{
    //Error: Cannot mark types with private, protected and protected internal access modifiers
    private class MainClass
    {
        public static void Main()
        {
            Console.WriteLine("This code will not compile");
        }
    }
}
Add 2 class library projects to the solution with names AssemblyOne and AssemblyTwo.

Copy and paste the following code in Class1.cs file of the AssemblyOne project.

namespace AssemblyOne
{
    //Class is marked internal. This class is available only within AssemblyOne
    internal class AssemblyOneClass
    {
        public void Print()
        {
            Console.WriteLine("Hello");
        }
    }
}

Copy and paste the following code in Class1.cs file of AssemblyTwo project.

using System;
using AssemblyOne;
namespace AssemblyTwo
{
    //Class is marked public. This class is available in any assembly
    public class AssemblyTwoClass
    {
        public void Print()
        {
            AssemblyOneClass instance = new AssemblyOneClass();
            instance.Print();
        }
    }
}

Add a reference to the AssemblyOne project, from the AssemblyTwo project. Please check the previous session, to learn about adding project references.

Now build the solution. You will notice the following 4 compiler errors.
  1. ‘AssemblyOne.AssemblyOneClass’ is inaccessible due to its protection level
  2. The type ‘AssemblyOne.AssemblyOneClass’ has no constructors defined
  3. ‘AssemblyOne.AssemblyOneClass’ is inaccessible due to its protection level
  4. ‘AssemblyOne.AssemblyOneClass’ does not contain a definition for ‘Print’ and no extension method ‘Print’ accepting a first argument of type ‘AssemblyOne.AssemblyOneClass’ could be found (are you missing a using directive or an assembly reference?)

All these errors are in the AssemblyTwo project and are related to AssemblyOne.AssemblyOneClass being inaccessible due to its protection level.

Now convert the access modifier of AssemblyOneClass from internal to the public and rebuild the solution. Now we will not get any errors. This shows that internal types are accessible only within the containing assembly.

Now just remove the public access modifier from AssemblyOneClass and rebuild the solution. Now again we get the same 4 errors that we got before. This is because if we don’t specify an access modifier for a type then by default the access modifier will be internal.

So if we don’t specify an access modifier then for Types the default is internal and for type members it is private.

In the next article, I am going to discuss Encapsulation in C# with examples. Here, in this article, I try to explain the Access Specifiers in C# using Type and Type Members in C# with some examples. I hope this article will help you with your needs. I would like to have your feedback. Please post your feedback, question, or comments about this article.

Leave a Reply

Your email address will not be published. Required fields are marked *