8
Access Modifiers
Public,
Private, Protected and Internal
Whenever a class is created by us we want to have the
ability to decide who can access certain members of the class. In other words,
we would like to restrict access to the members of the class. The basic rule is
that members of a class can freely access each other. There is no way one can
prevent a function of a particular class from executing another function in the
same class. By default though, the same class is allowed complete access but no
one else is granted access to the members of the class. The default access
modifier is private.
a.cs
class zzz
{
public static void Main()
{
yyy.pqr();
}
}
class yyy
{
static void abc()
{
System.Console.WriteLine(“yyy abc”);
}
public static void pqr()
{
System.Console.WriteLine(“yyy pqr”);
abc();
}
}
Output
yyy pqr
yyy abc
Pqr is public and hence anyone is allowed to execute it. abc
has no access modifier which makes it private, which is anyway the default.
The private modifier has no effect on
members of the same class and hence pqr is allowed to call abc. This concept is
called member access.
a.cs
class zzz
{
public static void Main()
{
yyy.abc();
}
}
class yyy
{
static void abc()
{
System.Console.WriteLine(“yyy abc”);
}
public static void pqr()
{
System.Console.WriteLine(“yyy pqr”);
abc(); } }
Compiler Error
a.cs(5,1): error CS0122: ‘yyy.abc()’ is inaccessible due to
its protection level
abc is private an no one but members of yyy can access it.
a.cs
class zzz
{
public static void Main()
{
yyy.abc();
}
}
class yyy
{
protected static void abc()
{
System.Console.WriteLine(“yyy abc”);
}
public static void pqr()
{
System.Console.WriteLine(“yyy pqr”);
abc();
} }
Compiler Error
a.cs(5,1): error CS0122: ‘yyy.abc()’ is inaccessible due to
its protection level
We have now introduced one more access modifier, protected,
which also does not let you access a class from outside. However pqr is allowed
to access abc as access modifiers do not effect the same class as mentioned
earlier.
a.cs
class zzz
{
public static void Main()
{
xxx.aaa();
}
}
class yyy
{
static void abc()
{
System.Console.WriteLine(“yyy abc”);
}
public static void pqr()
{
System.Console.WriteLine(“yyy pqr”);
}
protected static void xyz()
{
System.Console.WriteLine(“yyy xyz”);
}
}
class xxx : yyy
{
public static void aaa()
{
abc();
pqr();
xyz();
}
}
Compiler Error
a.cs(27,1): error CS0122: ‘yyy.abc()’ is inaccessible due to
its protection level
We are now dealing with
derived classes. When we flag a function with the modifier, protected,
we are informing C# that only derived classes can access the function. Nobody
else can. Thus in function aaa we can
call xyz as it is flagged protected, but it cannot be called from anywhere else
including Main. The function abc is private and can be called only from the
class yyy. Comment out abc(); in aaa and csc will show you no errors.
To sum up, we have learnt three concepts. Private means only
the same class has access, public means everyone has access and protected lies
in between where only derived classes have access.
All functions for example reside in a class. The
accessibility of that function is decided by the class in which it resides as
well as the modifiers on the function. If we are allowed access to a member, we
say that the member is accessible, else inaccessible.
b.cs
internal class yyy
{
}
csc /t:library b.cs
This command will produce a library b.dll with one class
yyy.
a.cs
class zzz
{
public static void
Main()
{
yyy a;
}
}
>csc a.cs /r:b.dll
Compiler Error
a.cs(5,1): error CS0122: ‘yyy’ is inaccessible due to its
protection level
We get the above error as the modifier internal means that
we can only access yyy from b.dll and not from any other file or program. Never
create a component and flag the class internal as no one would be able to use
it. Internal means access limited to this program only.
Also writing csc a.cs b.cs would not give us any error.
a.cs
public namespace vijay
{
class zzz
{
public static void
Main()
{
}
}
}
Compiler Error
a.cs(1,8): error CS1518: Expected class, delegate, enum,
interface, or struct
Namespaces by default can have no accessibility modifiers at
all. They are public by default and we cannot add any other access modifier
including public again.
a.cs
private class zzz
{
}
Compiler Error
a.cs(1,1): error CS1527: Namespace elements cannot be
explicitly declared as private or protected
A class can only be public or internal. It cannot be marked
as protected or private. The default is internal.
b.cs
class yyy
{
}
csc b.cs /t:library
Compiler Error
fatal error CS2022: Options ‘/out’ and ‘/target’ must appear
before source file names
Mistake, done on purpose. At times we will forget to tell
you that some compiler options like /t and /out must appear before the names of
the source files.
>csc /t:library b.cs
a.cs
class zzz
{
public static void
Main()
{
yyy a;
}
}
>csc a.cs /r:b.dll
Compiler Error
a.cs(5,1): error CS0122: ‘yyy’ is inaccessible due to its
protection level
Thus if we want other programs/files to access classes
created by us, we must not forget that by default they are marked as internal
as explained earlier.
Members of a class can have all the modifiers described
above and default to private.
a.cs
class zzz
{
public static void
Main()
{
}
public private void abc()
{
}
}
Compiler Error
a.cs(6,8): error CS0107: More than one protection modifier
You are not allowed more than one access modifier most of
the time. The exceptions, we will soon take care off. Predefined types like
int, object have no accessibility restrictions. They can be used anywhere and
everywhere.
b.cs
class yyy
{
public void abc()
{
}
}
csc /t:library b.cs
a.cs
class zzz
{
public static void
Main()
{
yyy a = new yyy();
a.abc();
}
}
Compiler Error
a.cs(5,1): error CS0122: 'yyy' is inaccessible due to its
protection level
a.cs(6,1): error CS0246: The type or namespace name 'a'
could not be found (are you missing a using directive or an assembly
reference?)
As the class yyy has not been specified by an access
modifier, it is by default internal. Even though abc is public, the type
enclosing it i.e. yyy is internal and hence no member of yyy can be accessed
from outside b.cs. Thus the access modifiers of the class and the members is important.
b.cs
public class yyy
{
void abc()
{
}
}
>csc /t:library b.cs
a.cs
class zzz
{
public static void
Main()
{
yyy a = new yyy();
a.abc();
}
}
Compiler Error
a.cs(6,1): error CS0122: ‘yyy.abc()’ is inaccessible due to
its protection level
Here yyy is accessible as the modifier is public but the
function abc is private and hence cannot be accessed by anyone but the class.
From now on we will only display a.cs and b.cs as the
command line executions of the compiler will remain the same. a.cs remains the
same for this program.
b.cs
public class yyy
{
internal void abc()
{
}
}
Compiler Error
a.cs(6,1): error CS0122: ‘yyy.abc()’ is inaccessible due to
its protection level
Internal means no one from outside the dll can access the
function.
a.cs
class zzz
{
public static void
Main()
{
ppp a = new ppp();
a.aaa();
}
}
b.cs
public class yyy
{
protected internal void abc()
{
}
}
public class xxx : yyy
{
void pqr()
{
abc();
}
}
public class ppp
{
public void aaa()
{
yyy a = new yyy();
a.abc();
}
}
No error occurs as protected internal means two things. It
is either derived classes or classes in the same file that can access abc.
Therefore derived class xxx can use it as well as class ppp.
What we are trying to get at here is that the containing
type decides first the accessibility and then the member modifiers also comes
in. Making the class internal and then the members public will in no way allow
classes in others files access it.
a.cs
class zzz
{
public static void
Main()
{
}
}
class yyy
{
protected int x;
void abc( yyy a , xxx b)
{
a.x = 1;
b.x = 2;
}
}
class xxx : yyy
{
void pqr( yyy a , xxx b)
{
a.x = 1;
b.x = 2;
} }
Compiler Error
a.cs(20,1): error CS1540: Cannot access protected member
‘yyy.x’ via a qualifier of type ‘yyy’; the qualifier must be of type ‘xxx’ (or
derived from it)
Class yyy contains a protected member x. To the same class
no modifiers make sense. However as x is protected, in the derived class
function pqr, we cannot access it through yyy as a.x gives us an error. However
b which looks like xxx does not give an error. To check this out, comment out
the line a.x=1 in pqr(). This means that we can access the protected members
not from an object of the base class, but from the derived class objects only.
This is in spite of the fact that x is a member of yyy, the base class. Even
so, we still cannot access it. Also we cannot access x from the function Main.
a.cs
class zzz
{
public static void
Main()
{
}
}
class yyy
{
}
public class xxx : yyy
{
}
Compiler Error
a.cs(10,14): error CS0060: Inconsistent accessibility: base
class ‘yyy’ is less accessible than class ‘xxx’
Between internal and public, public allows greater access to
its members. The class yyy is by default internal and xxx which derives from
yyy is explicitly made public. We get an error as the derived class yyy has to
have an access modifier which allows greater access than the base class access
modifier. Here internal is more restrictive than public.
a.cs
class zzz
{
public static void
Main()
{
}
}
public class yyy
{
}
class xxx : yyy
{
}
If we reverse the modifiers, i.e. we make yyy public and xxx
the derived class internal we get no error. The base class allows more
accessibility than the derived class.
a.cs
class zzz
{
public static void
Main()
{
}
}
class yyy
{
}
public class xxx
{
public yyy f()
{
return new yyy();
}
}
Compiler Error
a.cs(12,12): error CS0050: Inconsistent accessibility:
return type ‘yyy’ is less accessible than method ‘xxx.f()’
The accessibility of yyy is internal which is more
restrictive than public. The accessibility of function f is public which is
more than that of the type yyy. The error occurred as return values must
have greater accessibility than that of
the method, which is not true in this case.
a.cs
class zzz
{
public static void
Main()
{
}
}
class yyy
{
}
public class xxx
{
public yyy a;
}
Compiler Error
a.cs(12,12): error CS0052: Inconsistent accessibility: field
type ‘yyy’ is less accessible than field ‘xxx.a’
Rules are rules – they remain the same everywhere. The class
yyy or data type yyy is internal. a, an object/field is public which makes it
more accessible than yyy which is internal. Hence the error.
a.cs
class zzz
{
public static void
Main()
{
}
}
class yyy
{
}
public class xxx
{
yyy a;
}
Now we get no error as a has been made private which gives
it a lower accessibility than yyy which is internal. Logic is that whatever you
create must be more accessible than what you create from.
Sealed Classes
Sealed is another modifier that applies to classes. aaa is a
sealed class. No class can derive from aaa. In another words aaa cannot act as
a base class for any class.
a.cs
public class zzz
{
public static void Main()
{
}
}
sealed class aaa
{
}
class bbb : aaa
{
}
Compiler Error
a.cs(10,7): error CS0509: ‘bbb’ : cannot inherit from sealed
class ‘aaa’
a.cs
public class zzz
{
public static void Main()
{
aaa a = new aaa();
System.Console.WriteLine(a.i);
a.abc();
}
}
sealed class aaa
{
public int i = 9;
public void abc()
{
System.Console.WriteLine(“hi”);
}
}
Output
9
hi
The only difference between a sealed class and a non-sealed
class is that a sealed class cannot be derived from. Otherwise there is no
difference at all. It can contain the same variables, functions etc as a normal
class does . A sealed class lets us create classes which no one can derive
from. Thus the code in such classes cannot be overridden. Also as the compiler
knows this, certain run time optimizations can be performed on a sealed class
Constants
a.cs
public class zzz
{
const int i = 10;
public static void Main() {
System.Console.WriteLine(i);
}
}
Output
10
A constant or const variable behaves as a variable. We give
it an initial value and can use it wherever we can use a variable.
a.cs
public class zzz
{
const int i = 10;
public static void Main()
{
i++;
System.Console.WriteLine(i);
i = 30;
}
}
Compiler Error
a.cs(6,1): error CS0131: The left-hand side of an assignment
must be a variable, property or indexer
a.cs(8,1): error CS0131: The left-hand side of an assignment
must be a variable, property or indexer
Unlike a variable, we are not allowed to change the value of
a const. The change is an assignment statement.
a.cs
public class zzz
{
const int i ;
public static void Main()
{
i = 30;
System.Console.WriteLine(i);
}
}
Compiler Error
a.cs(3,13): error CS0145: A const field requires a value to
be provided
We have to initialize the const variable at the time of
creation. We are not allowed to initialize it later in our program.
a.cs
public class zzz
{
const int i = j + 4;
const int j = k - 1;
const int k = 3;
public static void Main()
{
System.Console.WriteLine(“{0} {1} {2}”,i,j,k);
}
}
Output
6 2 3
A constant can depend upon another constant. C# is smart
enough to realize that to calculate the value of const i, it first needs to
know the value of j. j’s value depends upon another const k, whose value is 3.
Thus C# first evaluates k to 3 then j becomes 2 i.e. k -1 and finally i takes
on the value of j i.e. 2 + 4 resulting
in 6.
Like classes const’s cannot be circular i.e., they cannot
depend upon each other.
a.cs
public class zzz
{
const int i = j + 4;
const int j = k - 1;
const int k = i;
public static void Main()
{
System.Console.WriteLine(“{0} {1} {2}”,i,j,k);
}
}
Compiler Error
a.cs(3,11): error CS0110: The evaluation of the constant
value for ‘zzz.i’ involves a circular definition
The value of the const i depends upon j which in turn
depends upon k, which is equal to i. This becomes a circular definition. A
const is a variable whose value cannot be changed but whose initial value is
compile time determined.
a.cs
public class zzz
{
public const aa a = new aa();
public static void Main()
{
}
}
public class aa
{
}
Compiler Error
a.cs(3,17): error CS0133: The expression being assigned to
‘zzz.a’ must be constant
a.cs
public class zzz
{
public const aa a = null;
public static void Main()
{
}
}
public class aa
{
}
The error vanishes as we are now initializing a to an object
which has a value that can be determined at compile time. We cannot ever change
the value of a, so it will always be null. Normally we do not have consts as a
reference type as they have value only at runtime.
As mentioned earlier we can only initialize a const to a
compile time value i.e. a value available to the compiler while it is
executing. new unfortunately gets executed at runtime and therefore has no
value at compile time. This gives us an error.
a.cs
class zzz
{
public static void Main()
{
yyy y = new yyy();
System.Console.WriteLine(y.i);
}
}
class yyy {
public const int i = 3;
}
Compiler Error
a.cs(6,26): error CS0176: Static member 'yyy.i' cannot be
accessed with an instance reference; qualify it with a type name instead
A constant is static by default and we cannot use the
instance reference i.e. a name to reference a const. A const has to be static
as no one is allowed to make any changes to a const.
a.cs
class zzz
{
public static void Main()
{
}
}
class yyy
{
public static const int i = 3;
}
Compiler Error
a.cs(9,25): error CS0504: The constant ‘yyy.i’ cannot be
marked static
C# does not want us to repeat the obvious over and over
again. Just like humans, programming language too have their own quirks. Some
other time, perhaps, C# may permit us to write a static before an entity that
is already static by default.
a.cs
class zzz
{
public static void Main()
{
System.Console.WriteLine(yyy.i + “ “ + xxx.i);
}
}
class yyy
{
public const int i = 3;
}
class xxx : yyy
{
public const int i = 30; }
Compiler Warning
a.cs(14,18): warning CS0108: The keyword new is required on
‘xxx.i’ because it hides inherited member ‘yyy.i’
Output
3 30
We can create a const with the same name as another const in
the base class. The const of the class xxx i will hide the const i in class yyy
for the class xxx only.
Fields
A field to start with is another word for a variable in a
class. There are a large number of generic rules that apply to all members of a
class and we will not tire you by repeating them ad nauseam.
A variable can never have an uninitialized value in C#.
a.cs
public class zzz
{
static int i;
static bool j;
public static void Main()
{
System.Console.WriteLine(zzz.i + “ “ + zzz.j );
} }
Output
0 False
Static variables are initialized when the class is loaded
first. An int is given an initial value of
zero and a bool False.
a.cs
public class zzz
{
int i;
bool j;
public static void Main()
{
zzz a = new zzz();
System.Console.WriteLine(a.i + “ “ + a.j );
} }
Output
0 False
An instance variable is initialized at the time of creation.
The keyword new will create an instance of
the zzz. It will allocate memory for each of the non static variables
and then initialize each of them to their default values.
a.cs
public class zzz
{
static int i = j + 10;
static int j = i + 1;
public static void Main()
{
System.Console.WriteLine(zzz.i + “ “ + zzz.j );
}
}
Output
10 11
Outputs make a lot of sense if you understand them in plain
simple English. C# always initializes static fields to their initial value
after creating them . Variables i and j are thus given a default of zero. Then
C# realizes that these variables need to be assigned some values. It does not
read all the lines, only one at a time. It will now read the first line and as
the variable j has a value of 0, i will get a value of 10. Then at the next
line, j is the value of i plus 1. The variable i has a value of 10 and j now
becomes 11. As it does not see both lines at the same time, it does not notice
the circularity of the above definition. In short, though the above example
works, it is frowned upon by the powers to be at C#.
a.cs
public class zzz
{
int i = j + 10;
int j = i + 1;
public static void Main()
{
}
}
Compiler Error
a.cs(3,9): error CS0236: A field initializer cannot
reference the nonstatic field, method, or property ‘zzz.j’
a.cs(4,9): error CS0236: A field initializer cannot
reference the nonstatic field, method, or property ‘zzz.i’
It does not work for instance variables as the rules of an
instance variable are different than that of static. The field initializer of
an instance variable has to be determined at the time of creation of the
object. The variable j does not have a value at this point in time. It cannot
refer to variables of the same instance at the time of creation. Thus we can
refer to no instance members to initialize an instance member. Textual order
means first come first served.
Readonly Fields
Fields can be also tagged with the modifier readonly.
a.cs
public class zzz
{
public static readonly
int i = 10;
public static void Main()
{
System.Console.WriteLine(i);
}
}
Output
10
No errors at all. However, remember if we use a non static
variable in a static function we will get an error.
a.cs
public class zzz
{
public static readonly
int i = 10;
public static void Main()
{
i = 20;
System.Console.WriteLine(i);
}
}
Compiler Error
a.cs(6,1): error CS0198: A static readonly field cannot be
assigned to (except in a static constructor or a variable initializer)
You cannot change the value of a readonly field after its
being given an initial value.
a.cs
public class zzz {
public static readonly
int i ;
public static void Main() {
}
}
Unlike a const, a readonly field does not have to be
initialized at the time of creation.
a.cs
public class zzz
{
public static readonly
int i ;
static zzz()
{
i = 20;
System.Console.WriteLine(“In Const”);
}
public static void Main()
{
System.Console.WriteLine(i);
}
}
Output
In Const
20
A static readonly field can be initialized in a static
constructor also. This is the major difference between a const and a readonly
field.
a.cs
public class zzz
{
public readonly aa a = new aa();
public static void Main()
{
}
}
public class aa
{
}
The same example which gave an error with const does not
give an error with readonly. To sum up a readonly is a more generic const and
it makes our programs more readable as we refer to a name and not a number. Is
100 more intuitive or priceofcopper easier to understand? The compiler would
for reasons of efficiency convert all const’s and readonly variables to the
actual values.
a.cs
public class zzz
{
public static void Main()
{
}
}
public class aa
{
public int readonly i = 10;
}
Compiler Error
a.cs(9,12): error CS1585: Member modifier ‘readonly’ must
precede the member type and name
a.cs(9,23): error CS1519: Invalid token ‘=’ in class,
struct, or interface member declaration
Wherever you can place multiple modifiers, remind yourself
that there are rules that decide the order of modifiers, which comes first.
Here the readonly modifier precedes the data type int. Once again, no great
cosmic law responsible, just a rule that must be remembered.
a.cs
public class zzz
{
public static void Main()
{
}
}
public class aa {
public readonly int i = 10;
void abc(ref int z)
{
}
void pqr()
{
abc(ref i);
}
}
Compiler Error
a.cs(13,9): error CS0192: A readonly field cannot be passed
ref or out (except in a constructor)
A readonly field cannot be changed by anyone except a constructor. The function abc expects a ref parameter which if you have forgotten allows you to change the value of the original. Thus C# does not permit a readonly as a parameter to a function that accepts a ref or a out parameters.