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 which are related to the C# access specifiers.

  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 (class and interface) as well as the scope of their members (variables, properties, and methods). That is who can access them and who cannot access them are defined by the Access Specifiers.

Types of Access Specifiers in C#:

C# supports 5 types of access specifiers. They are as follows

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

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

Understand Type and Type members in C#:

Before going to understand Access Specifier in C#, let us first understand what are Types and Type Members are. In the below example, Customer is the Type and variables (_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 variables, properties, constructors, methods, etc. that normally reside within a type are called type members. The Type members can have all the 5 access modifiers whereas types can have only 2 (internal, public) access modifiers

Note: 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. For this create a new console application with the name AccessSpecifierDemo. First, we will discuss Access Specifiers with the Type Members and then we will discuss Access Specifiers with the Type.

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 variable 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, the Id property is a public member. So, 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 of MainClass as shown below and it will not give us any error.
CustomerInstance.Id = 101;

Protected Access Specifier in C#:

Protected Members in C# are available within the containing type as well as 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 ID. CorporateCustomer 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.

  1. Using the derived class object: Console.WriteLine(corporateCustomerInstance.ID);
  2. Using this keyword: Console.WriteLine(this.ID);
  3. 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 Internal Access Specifier in C#, 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. So, To understand the Internal Access Specifier in C#, we need 2 assemblies. To generate 2 assemblies, we need to follow the below steps.

Right-click on the Solution Explorer and then select Add -> New Project option from the context menu as shown in the below image.

Access Specifiers in C#

Once you click on the New Project, it will open the following Add New Project Dialog Box. Here, select Visual C# from the Installed section, and then select Class Library from the center pane, and provide the name as AssemblyOne and finally click on the OK button as shown in the below image.

Access Specifiers in C# with Examples

Once you click on the OK button, it will add the Class Library Project with the name AssemblyOne to our solution. Again, repeat the process to create another class library project with the name AssemblyTwo to our solution. If you have followed the steps correctly, now we should have three projects in the solution explorer as shown in the below image.

Understand Private, Public, Protected, Internal, and ProtectedInternal access specifiers with examples

Now if we build the solution, we should have 3 assemblies generated. Two DLLs 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.
Creating Class in AssemblyOne Project:

Now, create a class file with the name AssemblyOneClass.cs within the AssemblyOne Project, and once you create the class file, then Copy and paste the following code into it.

namespace AssemblyOne
{
    public class AssemblyOneClassI
    {
        internal int ID = 999;
    }
    public class AssemblyOneClassII
    {
        public void Test()
        {
            AssemblyOneClassI instance = new AssemblyOneClassI();
            // Can access inetrnal member ID as both 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 this ID member from AssemblyOneClassII because this class is also present in the same assembly as AssemblyOneClassI. So, this proofs that the Internal Members access anywhere within the same assembly.

Creating Class in AssemblyTwp Project:

Now, create a class file with the name AssemblyTwoClass.cs within the AssemblyTwo Project, and once you create the class file, then Copy and paste the following code into it.

using AssemblyOne;
namespace AssemblyTwo
{
    public class AssemblyTwoClassI
    {
        public void Test()
        {
            AssemblyOneClassI instance = new AssemblyOneClassI();
            //Console.WriteLine(instance.ID);        
        }
    }
}

Notice, here, we got 2 compiler errors at this point. To solve this we need to add an assembly reference to AssemblyOne Project from AssemblyTwo Project. To do so, please follow the below steps.

  1. Expand the References folder under the AssemblyTwo project, from Solution Explorer.
  2. Right-click on the 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 on the OK button as shown in the below image.

Adding Project Reference in C#

At, this point all the compiler errors should have gone. Now, uncomment the following line from the AssemblyTwoClass.cs file from the AssemblyTwo project and rebuild the solution.
Console.WriteLine(instance.ID);

Now, we will get a compiler error as shown in the below image.

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 in C#:

Protected Internal Members in C# can be accessed anywhere within the assembly in which it is declared or from within a derived class in another assembly. So, we can think, 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 AssemblyOneClass.cs file in the AssemblyOne project.

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

And then, modify the code in the AssemblyTwoClass.cs file in the AssemblyTwo project as shown below. As you can see, now the AssemblyTwoClassI is derived from the AssemblyOneClassI.

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 proofs that the 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 in C#:

We can use all 5 access specifiers with type members in C# but type allows only Internal and Public access specifiers. It is a compile-time error to use private, protected, and protected internal access specifiers 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");
        }
    }
}

We are also going to work with the same class library projects that we have created already. Copy and paste the following code in the AssemblyOneClass.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");
        }
    }
}

Now, copy and paste the following code in the AssemblyTwoClass.cs file of the 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();
        }
    }
}

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 Public and rebuild the solution. Now we will not get any errors. This proofs 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 specifier for a type then by default the access modifier will be internal.

Note: If we don’t specify an access specifier in C# 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 with examples. I hope you enjoy this article and understand the C# Access Specifiers. I would like to have your feedback. Please post your feedback, question, or comments about this Access Specifiers in C# with Examples article.

2 thoughts on “Access Specifiers in C#”

  1. blank

    Your explanation is very good .

    Could you please double check below statement in quotes

    “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”

    Actually Elements defined in a namespace cannot be explicitly declared as private, protected, protected internal, or private protected ,It is possible to have a private class inside public class

  2. blank

    The author might have mentioned the access modifiers for Types directly inside the namespace. (with internal and public)

Leave a Reply

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