Access Modifiers in C#

Access Modifiers

Classes and structs can be restricted so that only the program or namespace they are declared in may use them. Class members can be restricted so that only derived classes can use them, or restricted so that only classes within the current namespace or program can use them. Access modifiers are keywords added to the class, struct, or member declaration to specify these restrictions. Those keywords are public, private, protected, and internal. For example:

C#

public class Bicycle
{
public void Pedal() { }
}

Classes and structs that are not nested within other classes or structs can be either public or internal. A type declared as public is accessible by any other type. A type declared as internal is only accessible by types within the same assembly. Classes and structs are declared as internal by default unless the keyword public is added to the class definition, as in the previous example. Class or struct definitions can add the internal keyword to make their access level explicit. Access modifiers do not affect the class or struct itself — it always has access to itself and all of its own members.

Class or struct members can be declared with one of five types of access. They can be public or internal, like the classes and structs themselves. A class member can be declared as protected using the protected keyword, meaning that only derived types using the class as a base can access the member. By combining the protected and internal keywords, a class member can be marked protected internal — only derived types or types within the same assembly can access that member. Finally, a class or struct member can be declared as private with the private keyword, indicating that only the class or struct declaring the member is allowed access to that member.

To set the access level for a class or struct member, add the appropriate keyword to the member declaration. Some examples:

C#

// public class:
public class Tricycle
{
// protected method:
protected void Pedal() { }

// private field:
private int m_wheels = 3;

// protected internal property:
protected internal int Wheels
{
get { return m_wheels; }
}
}

Interfaces, like classes, can be declared as public or internal types. Unlike classes, interfaces default to internal access. Interface members are always public, and no access modifiers can be applied.

Namespaces and enumeration members are always public, and no access modifiers can be applied.

Delegates have internal access by default.

Any types declared within a namespace or at the top level of a compilation unit (for example, not within a namespace, class, or struct) are internal by default, but can be made public.

Nested Types in C#

Nested Types

A type defined within a class or struct is called a nested type. For example:

C#

class Container
{
class Nested
{
Nested() { }
}
}

Regardless of whether the outer types is a class or a struct, nested types default to private, but can be made public, protected internal, protected, internal, or private. In the previous example, Nested is inaccessible to external types, but can be made public like this:

C#

class Container
{
public class Nested
{
Nested() { }
}
}

The nested, or inner type can access the containing, or outer type. To access the containing type, pass it as a constructor to the nested type. For example:

C#

public class Container
{
public class Nested
{
private Container m_parent;

public Nested()
{
}
public Nested(Container parent)
{
m_parent = parent;
}
}
}

Nested types can access private and protected members of the containing type, including any inherited private or protected members.

In the previous declaration, the full name of class Nested is Container.Nested. This is the name used to create a new instance of the nested class, as follows:

C#

Container.Nested nest = new Container.Nested();

Constants In C#

Constants

Classes and structs can declare constants as members. Constants are values which are known at compile time and do not change. (To create a constant value that is initialized at runtime, use the readonly keyword.) Constants are declared as a field, using the const keyword before the type of the field. Constants must be initialized as they are declared. For example:

C#

class Calendar1
{
public const int months = 12;
}

In this example, the constant months will always be 12, and cannot be changed — even by the class itself. Constants must be of an integral type (sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, or string), an enumeration, or a reference to null.

Multiple constants of the same type can be declared at the same time, for example:

C#

class Calendar2
{
const int months = 12, weeks = 52, days = 365;
}

The expression used to initialize a constant can refer to another constant so long as it does not create a circular reference. For example:

C#

class Calendar3
{
const int months = 12;
const int weeks = 52;
const int days = 365;

const double daysPerWeek = days / weeks;
const double daysPerMonth = days / months;
}

Constants can be marked as public, private, protected, internal, or protected internal. These access modifiers define how users of the class can access the constant. For more information, see Access Modifiers (C# Programming Guide).

Constants are accessed as if they were static fields, although they cannot use the static keyword. Expressions that are not contained within the class defining the constant must use the class name, a period, and the name of the constant to access the constant. For example:

C#

int birthstones = Calendar.months;

Fields in C#

Fields

This topic describes fields, which are objects or values contained in a class or struct. Fields allow classes and structures to encapsulate data.

For simplicity, these examples use fields that are public, but this is not recommended in practice. Fields should generally be private. Access to fields by external classes should be indirect, by means of methods, properties, or indexers. For more information, see Methods, Properties and Indexers.

Fields store the data a class needs to fulfill its design. For example, a class representing a calendar date might have three integer fields: one for the month, one for the day, and one for the year. Fields are declared within the class block by specifying the access level of the field, followed by the type of the field, followed by the name of the field. For example:

C#
public class CalendarDate
{
public int month;
public int day;
public int year;
}

Accessing a field in an object is done by adding a period after the object name, followed by the name of the field, as in objectname.fieldname. For example:

C#
CalendarDate birthday = new CalendarDate();
birthday.month = 7;

A field can be given an initial value by using the assignment operator when the field is declared. To automatically assign the month field to 7, for example, you would declare month like this:

C#
public class CalendarDateWithInitialization
{
public int month = 7;
//...
}

Fields are initialized immediately before the constructor for the object instance is called, so if the constructor assigns the value of a field, it will overwrite any value given during field declaration. For more information, see Using Constructors.

NoteNote

A field initializer cannot refer to other instance fields.

Fields can be marked as public, private, protected, internal, or protected internal. These access modifiers define how users of the class can access the fields. For more information, see Access Modifiers.

A field can optionally be declared static. This makes the field available to callers at any time, even if no instance of the class exists. For more information, see Static Classes and Static Class Members.

A field can be declared readonly. A read-only field can only be assigned a value during initialization or in a constructor. A static readonly field is very similar to a constant, except that the C# compiler does not have access to the value of a static read-only field at compile time, only at run time. For more information, see Constants.

Using Constructors In C#

Using Constructors

Constructors are class methods that are executed when an object of a given type is created. Constructors have the same name as the class, and usually initialize the data members of the new object.

In the following example, a class called Taxi is defined with a simple constructor. This class is then instantiated with the new operator. The Taxi constructor is invoked by the new operator immediately after memory is allocated for the new object.

C#
public class Taxi
{
public bool isInitialized;
public Taxi()
{
isInitialized = true;
}
}

class TestTaxi
{
static void Main()
{
Taxi t = new Taxi();
System.Console.WriteLine(t.isInitialized);
}
}

A constructor that takes no parameters is called a default constructor. Default constructors are invoked whenever an object is instantiated using the new operator and no arguments are provided to new. For more information, see Instance Constructors.

Unless the class is static, classes without constructors are given a public default constructor by the C# compiler in order to enable class instantiation. For more information, see Static Classes and Static Class Members.

You can prevent a class from being instantiated by making the constructor private, as follows:

C#
class NLog
{
// Private Constructor:
private NLog() { }

public static double e = System.Math.E; //2.71828...
}

For more information, see Private Constructors.

Constructors for struct types are similar to class constructors, but structs cannot contain an explicit default constructor because one is provided automatically by the compiler. This constructor initializes each field in the struct to the default values shown in the Default Values Table. However, this default constructor is only invoked if the struct is instantiated with new. For example, this code uses the default constructor for Int32, so you are assured that the integer is initialized:

int i = new int();
Console.WriteLine(i);

The following code, however, results in Compiler Error CS0165 because it does not use new, and because it attempts to use an object that has not been initialized:


int i;
Console.WriteLine(i);

Alternatively, objects based on structs can be initialized or assigned and then used, like this:

int a = 44;  // Initialize the value type...
int b;
b = 33; // Or assign it before using it.
Console.WriteLine("{0}, {1}", a, b);

So calling the default constructor for a value type is not required.

Both classes and structs can define constructors that take parameters. Constructors that take parameters must be called through a new statement or a base statement. Classes and structs can also define multiple constructors, and neither is required to define a default constructor. For example:

C#
public class Employee
{
public int salary;

public Employee(int annualSalary)
{
salary = annualSalary;
}

public Employee(int weeklySalary, int numberOfWeeks)
{
salary = weeklySalary * numberOfWeeks;
}
}

This class can be created using either of the following statements:

C#
Employee e1 = new Employee(30000);
Employee e2 = new Employee(500, 52);

A constructor can use the base keyword to call the constructor of a base class. For example:

public class Manager : Employee
{
public Manager(int annualSalary)
: base(annualSalary)
{
//Add further instructions here.
}
}

In this example, the constructor for the base class is called before the block for the constructor is executed. The base keyword can be used with or without parameters. Any parameters to the constructor can be used as parameters to base, or as part of an expression. For more information, see base.

In a derived class, if a base-class constructor is not called explicitly using the base keyword, then the default constructor, if there is one, is called implicitly. This means that the following constructor declarations are effectively the same:

C#
public Manager(int initialdata)
{
//Add further instructions here.
}
C#
public Manager(int initialdata) : base()
{
//Add further instructions here.
}

If a base class does not offer a default constructor, the derived class must make an explicit call to a base constructor using base.

A constructor can invoke another constructor in the same object using the this keyword. Like base, this can be used with or without parameters, and any parameters in the constructor are available as parameters to this, or as part of an expression. For example, the second constructor in the previous example can be rewritten using this:

C#
public Employee(int weeklySalary, int numberOfWeeks)
: this(weeklySalary * numberOfWeeks)
{
}

The use of the this keyword above causes this constructor to be called:

C#
public Employee(int annualSalary)
{
salary = annualSalary;
}

Constructors can be marked as public, private, protected, internal, or protected internal. These access modifiers define how users of the class can construct the class. For more information, see Access Modifiers.

A constructor can be declared static using the static keyword. Static constructors are called automatically, immediately before any static fields are accessed, and are normally used to initialize static class members. For more information, see Static Constructors.

Destructors in C#

Destructors are used to destruct instances of classes.

  • Destructors cannot be defined in structs. They are only used with classes.

  • A class can only have one destructor.

  • Destructors cannot be inherited or overloaded.

  • Destructors cannot be called. They are invoked automatically.

  • A destructor does not take modifiers or have parameters.

For example, the following is a declaration of a destructor for the class Car:

C#
class Car
{
~ Car() // destructor
{
// cleanup statements...
}
}

The destructor implicitly calls Finalize on the object's base class. Therefore, the preceding destructor code is implicitly translated to:

protected override void Finalize()
{
try
{
// cleanup statements...
}
finally
{
base.Finalize();
}
}

This means the Finalize method is called recursively for all of the instances in the inheritance chain, from the most-derived to the least-derived.

NoteNote

Empty destructors should not be used. When a class contains a destructor, an entry is created in the Finalize queue. When the destructor is called, the garbage collector is invoked to process the queue. If the destructor is empty, this simply results in a needless loss of performance.

The programmer has no control over when the destructor is called because this is determined by the garbage collector. The garbage collector checks for objects that are no longer being used by the application. If it considers an object eligible for destruction, it calls the destructor (if any) and reclaims the memory used to store the object. Destructors are also called when the program exits.

It is possible to force garbage collection by calling Collect, but in most cases, this should be avoided because it may result in performance issues. For more information, see Forcing a Garbage Collection.

In general, C# does not require as much memory management as is needed when developing with a language that does not target a runtime with garbage collection. This is because the .NET Framework garbage collector implicitly manages the allocation and release of memory for your objects. However, when your application encapsulates unmanaged resources such as windows, files, and network connections, you should use destructors to free those resources. When the object is eligible for destruction, the garbage collector runs the object's Finalize method.

If your application is using an expensive external resource, it is also recommended that you provide a way to explicitly release the resource before the garbage collector frees the object. You do this by implementing a Dispose method from the IDisposable interface that performs the necessary cleanup for the object. This can considerably improve the performance of the application. Even with this explicit control over resources, the destructor becomes a safeguard to clean up resources if the call to the Dispose method failed.

For more details on cleaning up resources, see the following topics:

The following example creates three classes that make a chain of inheritance. The class First is the base class, Second is derived from First, and Third is derived from Second. All three have destructors. In Main(), an instance of the most-derived class is created. When the program runs, notice that the destructors for the three classes are called automatically, and in order, from the most-derived to the least-derived.

C#
class First
{
~First()
{
System.Console.WriteLine("First's destructor is called");
}
}

class Second: First
{
~Second()
{
System.Console.WriteLine("Second's destructor is called");
}
}

class Third: Second
{
~Third()
{
System.Console.WriteLine("Third's destructor is called");
}
}

class TestDestructors
{
static void Main()
{
Third t = new Third();
}
}

Third's destructor is called

Second's destructor is called

First's destructor is called

Copy Constructor in C#

Unlike some languages, C# does not provide a copy constructor. If you create a new object and want to copy the values from an existing object, you have to write the appropriate method yourself.

Example

In this example, the Person class contains a constructor that takes as the argument another object of type Person. The contents of the fields in this object are then assigned to the fields in the new object.

C#
class Person
{
private string name;
private int age;

// Copy constructor.
public Person(Person previousPerson)
{
name = previousPerson.name;
age = previousPerson.age;
}

// Instance constructor.
public Person(string name, int age)
{
this.name = name;
this.age = age;
}

// Get accessor.
public string Details
{
get
{
return name + " is " + age.ToString();
}
}
}

class TestPerson
{
static void Main()
{
// Create a new person object.
Person person1 = new Person("George", 40);

// Create another new object, copying person1.
Person person2 = new Person(person1);
System.Console.WriteLine(person2.Details);
}
}

Output

George is 40