Introduction
This article is for beginners who have just started programming. Here I will show you how access modifiers help us design a class and when and why we use which modifier.
Background
If you search for the words "access modifiers" on the internet, you will find lots of references. I did the same and
I am showing the below from MSDN.
public
: Access is not restricted.
private
: Access is limited to the containing type.
Internal
: Access is limited to the current assembly.
protected
: Access is limited to the containing class or types derived from the containing class.
protected internal
: Access is limited to the current assembly or types derived from the containing class.
Using the Code
So let's start step by step by using a case study.
- Case: Design a
Customer
class that will have id
,
name
, reward-point
variables. Id
will be a random number; for general customer the reward point will always be 500, and for
VIP customer it will be 1500. Also a print
method is needed for each customer type which will show the output of the customer.
- Analysis: So from the requirement, if we separate things, then we find the work to do is:
Customer
class with {private Id, public: Name, protected : RewardPoint}
GenaralCustomer
and VIPCustomer
which inherits Customer
Class
- Client for each customer type class which gives the name of the customer and calls the
print
method.
The below code is for analysis #1:
namespace SameAssembly
{
public class Customer
{
private int Id {get;set;}
public string Name {get;set;}
protected int RewardPoint{get;set;}
public Customer()
{
Id = new Random().Next();
}
internal virtual void PrintCustomerInfo()
{
Console.WriteLine("\t\t\t\t ID:{0} \n\n \t\t\t\t
Name:{1} \n\n \t\t\t\t RewardPoint:{2} \n\n", Id, Name, RewardPoint);
}
}
}
The below code is for analysis #2 for GeneralCustomer
:
namespace SameAssembly
{
public class GeneralCustomer : Customer
{
internal string CustomerType
{
get { return "******** FOR GENERAL CUSTOMER ****************"; }
}
public GeneralCustomer()
{
RewardPoint = 500;
}
internal override void PrintCustomerInfo()
{
Console.WriteLine("\n\n\t\t\t" + CustomerType + "\n\n\t\t\t");
base.PrintCustomerInfo();
}
}
}
The below code is for analysis #3 client for GeneralCustomer
:
namespace SameAssembly
{
public class ClientForGeneral
{
public void Print()
{
var generalCustomer = new GeneralCustomer();
generalCustomer.Name = "Mr Jhon";
generalCustomer.PrintCustomerInfo();
}
}
}
If you look at the above Customer
class, you see that ID is private
. Why? Because we will not allow any other class to access it and also not even from the instance of the Customer
class,
we can only access it from the body of the Customer
class where it is declared. So we assign it in the constructor of the Customer
class.
In short, we say "Access is limited to the containing type".
Name
is public
. Why? Because we allow access from everywhere. It can be accessed from
a derived type or by an instance of Customer
in the same assembly or a different assembly and also from the same content type.
in short, we say "Access is not restricted".
Rewardpoint
is protected
. Why? Because we allow the access from its own containing class and also from the derive type in any assembly but not
from the instance of base or derive type. One word we say "Access is limited to the containing class * or types derived from the containing class.";
PrintCustomerInfo()
is internal
. Why? Because we allow the access in the same assembly so that any class that is in the same assembly
as the Customer
class
that inherits Customer
will be able to override the method.
In short, we
say "Access is limited to the current assembly".
What does same assembly and different assembly mean?
Look at the picture below. Here my Customer
class and Generalcustomer
class are in the same assembly. Both the classes are in the same
Class Library named SameAssembly
and also share the same namespace. So we can say they are in
the same assembly. ClientForVip
and VipCustomer
are in the same namespace/library. If Customer
class is used in the VipCustomer
class, then we can say that it is using different assemblies.
Notice both the members of the Customer
class rewardpoint
and PrintCustomerInfo
can easily be accessed from
the GeneralCustomer
class. rewardPoint
is protected
so it can easily
be accessed from the derived type and PrintCustomerInfo
is internal
so GeneralCustomer
can access it because it is in the same assembly.
The below code is for analysis #2 for VIPlCustomer
:
The code is:
namespace DifferentAssembly
{
public class VipCustomer : Customer
{
internal string CustomerType
{
get
{
return "******** FOR VIP CUSTOMER ****************";
}
}
public VipCustomer()
{
RewardPoint = 1500;
}
internal override void PrintCustomerInfo()
{
Console.WriteLine("\n\n\t\t\t" + CustomerType+ "\n\n\t\t\t");
base.PrintCustomerInfo();
}
}
}
Here is the trick. I place the VIPCustomer
class in a different class library than
the Customer
class. From there, I want to access my Customer
class members in
the derived class VIPCustomer
. So will the above code compile? No, it will give you the below compiled error:
Error 1 'DifferentAssembly.VipCustomer.PrintCustomerInfo()':
no suitable method found to override
J:\Access Modifiers\AccessModifiers\DifferentAssembly\VipCustomer.cs 18 33 DifferentAssembly
Why? Because rewardpoint
is OK, accessible from any derived type as it is protected
.
But PrintCustomerInfo
can't access from another assembly as it is defined as internal
. As I said earlier:
PrintCustomerInfo()
is internal. Why? Because we allow the access in the same assembly, so that any class that is in the same assembly
as the customer
class
that inherits customer
class will be able to override the method.
So what's the solution? Will we make it protected
, does it solve our issue? No, not at all. It will raise another issue on our client class ClientForGeneral
because we can't access PrintCustomromerInfo
from there if it's protected
. The below line will give an error.
generalCustomer.PrintCustomerInfo();
So what's the solution? The solution is protected internal
.
We need to change the PrntCustomerInfo
method of the Customer
class like below:
protected internal virtual void PrintCustomerInfo()
{
Console.WriteLine("\t\t\t\t ID:{0} \n\n \t\t\t\t
Name:{1} \n\n \t\t\t\t RewardPoint:{2} \n\n", Id, Name, RewardPoint);
}
In the current assembly, the client ClientForGeneral
will have
no problem at all to access PrintCustomerInfo
through the object because it's in
the same assembly; here it will act as internal
.
So the override method in GeneralCustomerClass
will be like below:
protected internal override void PrintCustomerInfo()
{
Console.WriteLine("\n\n\t\t\t" + CustomerType+ "\n\n\t\t\t");
base.PrintCustomerInfo();
}
In VipCustomer
, it will act as protected
like below.
Here you can't use protected internal
; only protected
modifier is valid here.
protected override void PrintCustomerInfo()
{
Console.WriteLine("\n\n\t\t\t" + CustomerType+ "\n\n\t\t\t");
base.PrintCustomerInfo();
}
If you write it protected internal
, you will receive the compile error:
Error 1 'DifferentAssembly.VipCustomer.PrintCustomerInfo()': cannot change access
modifiers when overriding 'protected' inherited member
'SameAssembly.Customer.PrintCustomerInfo()'
J:\Access Modifiers\AccessModifiers\DifferentAssembly\VipCustomer.cs 17 42 DifferentAssembly
So the compiler will easily assume the derived type, it will act as protected
not protected internal.
Points of Interest
What about the client class ClientForVip
? How it will access the
PrintCustomerInfo()
method as it is now protected
? We need to give some extra effort,
we need another method that is accessible through that class instance.
internal void PrintVipCustomerInfo()
{
PrintCustomerInfo();
}
Now the client class can access PrintCustomerInfo
through its internal method PrintVipCustomerInfo
.
namespace DifferentAssembly
{
class ClientForVip
{
public void Print()
{
var generalCustomer = new VipCustomer();
generalCustomer.Name= "Mr Mark";
generalCustomer.PrintVipCustomerInfo();
}
}
}
If we add the reference of SameAssembly
and DifferentAssembly
to any console application and call the client of each type like below:
namespace AccessModifiers
{
class Program
{
static void Main(string[] args)
{
var generalClient = new ClientForGeneral();
generalClient.Print();
var vipClient = new ClientForVip();
vipClient.Print();
Console.ReadKey();
}
}
}
It gives the following output:
I found most of developers confused about the use of internal
and protected internal
. Hope this article helps them to clear their understanding about Access Modifiers.