C# Access Modifiers

Introduction

Modifiers are C# keywords used to modify declarations of types (class, struct, interface, enum) and type members (fields, properties, methods, indexers, …). The remaining sections explain these modifiers.

Access Modifiers

Access modifiers are keywords used to specify the declared accessibility of types and type members. The four access modifiers are discussed in the table below:

Access ModifierMeaning
publicpublic is an access modifier for types and type members. This is the most permissive access level as there are no restrictions on accessing a public type or type member.
internalinternal is an access modifier for types and type members. internal members are accessible only within file of the same assembly. It is an error to reference an internal type or internal type member outside the assembly within which it was declared.

A common use of internal access is in component-based development because it enables a group of components to interact in a private matter without being exposed to the outer world. For example, a Data Access Layer could have several classes with internal members that are only used by the DAL.
protectedprotected is an access modifier for type members only. A protected member is only accessible within the body of the containing type and from within any classes derived from the containing type.
privateprivate is an access modifier for type members only. private access is the least accessible and such members are only accessible within the body of the containing type.Note that nested types within the same containing body can also access those private members.

Accessibility Levels

The four access modifiers listed above result in five accessibility levels shown below. Note how certain accessibility levels are not permitted depending on the context in which an access modifiers was used:

Accessibility LevelMeaningApplies To NamespacesApplies To TypesApplies To Type Members
publicAccess is not restricted.YesYesYes
internalAccess is limited to the current assembly (project).NoYesYes
protectedAccess is limited to the containing class, and classes derived from the containing classNoNoYes
internal protectedAccess is limited to the current assembly (project), the containing class, and classes derived from the containing classNoNoYes
privateAccess is limited to the containing type.NoNoYes

The following table illustrates accessibility levels for namespaces, types, and type members. Note that namespace elements (i.e., classes, structs, interfaces and enum) can only have public or internal declared accessibility. Access modifiers private, protected,  and protected internal are not allowed on namespace members. 

ContextDefault AccessibilityAllowed Declared AccessibilityDefault Member AccessibilityAllowed Declared Accessibility On Members
namespacepublicNoneInternalpublic
internal
classinternal (if top level)public
internal
privatepublic
protected
internal
internal protected
private
interfaceinternal (if top level)public
internal
publicNone
structinternal (if top level)public
internal
privatepublic
internal
private
enuminternal (if top level)public
internal
pubicNone

abstract

Applies To: Classes, methods, and properties.

The meaning of the keyword abstract depends on the context in which it is used. The abstract keyword can be used in three contexts:

  • classes
  • methods
  • properties

Abstract Classes

An abstract class means that the class is intended to be used as a base class only. Note the following features of abstract classes:

  • An abstract class cannot be instantiated.
  • An abstract class may contain abstract methods and properties.
  • A non-abstract class that derives from an abstract class must implement all inherited abstract methods and properties. (if any).
  • An abstract class must provide implementation for all interface members.

public abstract class Person
{
    … 
}

Abstract Methods and Properties

An abstract method or property means that the method (or property) does not have an implementation. Note the following features of abstract methods and properties:

  • An abstract method/property is implicitly virtual.
  • Abstract method/property declarations are only permitted in abstract classes.
  • An abstract method/property has no implementation body.
  • The implementation of an abstract method/property must be provided by an overriding method that is a member of class deriving from the abstract class containing the abstract method.
  • It is an error to use static, virtrual, or override in an abstract method declaration.

The following class illustrates the above points:

public interface IFile
{
    void Open();
    void Close();
}

public abstract class Folder : IFile
{
    // Data members
    long lSize;

    // Interface members must be implemented, even if the containing class is abstract
    public void Open() { Trace.WriteLine( “IFile.Open” ); }
    public void Close() { Trace.WriteLine( “IFile.Close” ); }

    // Abstract members and properties
    public abstract void RenameFolder();
    public abstract string FolderName
    { 
        get;
        set;
    }

    // Non-abstract methods/properties
    public long FolderSize
    {
        get { return 0; }
        set { lSize = value; }
    }
}

public class WindowsFolder : Folder
{
    // Data members
    string strFolderName; 

    // Implement inherited abstract methods
    public override void RenameFolder()
    {
        Trace.WriteLine( “WindowsFolder.RenameFolder” );
    }

    public override string FolderName
    { 
        get { return strFolderName; }
        set { strFolderName = value; }
    }
}

const

Applies To: fields and member variables.

The keyword const is used to modify the declaration of a field or local variables. It indicates that the value of the field or local variable cannot be modified. Note the following two points for using const:

  • The constant expression used to initialize the constant must yield a value of the target type, or a value of a type that can be implicitly converted to the target type.
  • The constant expression used to initialize the constant must be one that can be fully evaluated at compile time. Therefore, the only allowable value for a reference type is either a string or null.
  • static is not allowed on a constant expression.

public class ClassWithConstants
{
    // const is applied to fields.
    private const double PI         = 3.14;             // A const
    private const double CM_To_Inch = 2.54;             // A const
    private const double Inch_To_CM = 1 / CM_To_Inch;   // A const can participate in a const expression
    private const string USER      = null;              // reference types must be initialized either to null or to a string

    public void ConvertFromEnglishToMetric()
    {
        // const can also be applied to local variables
        const double Meter_To_Inch = CM_To_Inch * 100;

        …
    }
}

extern

Applies To: Method, property, event, indexer, operator, constructor, and destructor 

The extern modifier is used in a class member declaration (method, property, event, indexer, operator, constructor, and destructor ) to indicate that the method is implemented somewhere else outside the C# code (externally). extern is usually used with the DllImport attribute. And because an extern method should not provide an implementation, there is no method body. For example:

public class MyClass
{
    [DllImport(“User32.dll”)]
    public static extern GetDirectoryName( out string strDirName );

    public void ShowDirectoryName()
    {
        string sName;
        GetDirectoryName( out sName );       // Use the extern method just like any other C# method
        return sName;
    }
}

It is an error to use extern and abstract modifiers to modify the same member. abstract modifier means that the method implementation is not provided in the containing class but should be provided in derived class, whereas extern means that the method is implemented outside the C# code.

External Assembly Alias (New in C# 2.0)

Sometimes you may have to use two or more versions of the same assembly in the same application. In other words, you need to reference two versions of the same assembly that have the same fully-qualified name. By using an external assembly alias, the namespaces of each assembly can be wrapped inside root-level namespaces named by the alias, allowing them to be used in the same file.

External assembly aliases are accomplished with the alias sub-option of the /Reference compiler option. The main procedure is as follows: Suppose you have a Data Access Layer project called DAL written in VB. You compile this project to produce DAL_VB.DLL. This DLL is in turn used by a business objects layer DLL called BOL.DLL. You then rewrite DAL in C# but only manage to partially finish it. This partially finished code is now in a DLL called DAL_CS.DLL. Both DAL_VB.DLL and DAL_CS.DLL refer to the code base which has the same namespaces. In your BOL.DLL you still would like to reference both DLLs – DAL_CS.DLL for the new and improved functionality, and DAL_VB.DLL for the remaining yet-to-translate functionality.

You then compile your BOL.DLL with the /Reference option as follows:

/Reference DALVB=DAL_VB.DLL /Reference DALCS=DAL_CS.DLL

This will setup two external references called DALVB and DALCS, which you can use in the BOL code via an extern statement as follows:

extern DALVB;
extern DALCS;

// You can now refer to the same class from both DLLs
DALVB.DAL.ExecuteNonQuery( … );            // Old DLL
DALCS.DAL.ExecuteNonQuery( … );            // New DLL

override

Applies To : Method, property, event, indexer.

An override class member provides a new implementation for a member derived from a base class. Note the following points:

  • The overridden method in the derived class must have the same exact signature as the inherited class member.
  • The inherited class member to be overridden must be abstract, virtual, or override.
  • You cannot override a static or non-virtual method.

Partial (C# 2.0)

Applies to: classes, structs and interfaces. Does not apply to delegate and enums.

partial keyword is used to split the definition of a class or struct or interface across multiple source files. Each source file would contain a section of the class definition and all parts are combined when the application is compiled. The following example illustrates:

// File1.cs
public partial class Order
{
    private GetOrderDetails() {  }
}

// File2.cs
public partial class Order
{
    public RetrieveOrder()
    {
        GetOrderDetails();
        
    }
}

 Using partial is desirable in the following situations.

  • In large projects spreading a class definition over multiple source files allows multiple programmers to work on it simulataneously.
  • When working with automatically generated source code, your code can be added to the automatically generated source code (in a separate class using partial keyword) without having to edit the automatically generated source code. Visual Studio uses this functionality when creating Windows Forms (and other file types). Consider this code generated by VS.NET 2005 for a Windows Forms class:

// MyForm.cs. Your code goes into this class

public partial class MyForm : Form
{
    public MyForm()
    {
        InitializeComponent();
    }

    // Your code would be added here …
}

// MyForm.Designer.cs. Automatically generated code maintained by VS.NET as you add/remove controls

partial class MyForm
{
    private System.ComponentModel.IContainer components = null;

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code
    …
    #endregion
}

Note the following points about the partial keyword:

  • All the parts of a partial definition must use the partial keyword, and all the parts must be available at compile time to form the final type. Obviously, all parts must have the same declared accessibility ( a partial part cannot be public while another is internal).
  • All parts of a partial definition must be defined in the same assembly.
  • If any of the parts is declared abstract, then the entire type is abstract. Likewise, if any of the parts is declared sealed, then the entire type is sealed.
  • If any of the parts inherits from a base class, then the entire type inherits from that class.
  • Each partial part my inherit from a different interface, but the final type must implement all the interfaces listed by the partial class definitions.
  • Any class, struct, or interface members declared in a partial definition are available to all the other parts.
  • Nested types can be partial, even if the containing type is not partial

public class MyClass
{
    partial class MyPartialClass
    {
        
    }

    …

    partial class MyPartialClass
    {
        
    }
}

  • At compile-time, attributes of partial-type definitions are merged together.

[Synchronized]
public partial class MyClass { … }

[Serializable]
public partial class MyClass { … }

// Above two code fragments are equivalent to
[Synchronized]
[Serializable]
public partial class MyClass { … }

  • The following are merged together for all partial class definitions: XML comments, interfaces, generic-type parameter attributes, class attributes, and members. For example:

public partial class Car : IEngine, IBody
{ … }

public partial class Car : Vehicle
{ … }

// Above two code fragments are equivalent to
public partial class Car : Vehicle, IEnging, IBody
{ … }

readonly

Applies To: fields

readonly is a modifier that can only be applied to class fields. A readonly field cannot be assigned a value during program execution except as part of its declaration or in a constructor of the same class.  See readonly in Classes section for a full example.

sealed

Applies To: class methods, indexers, properties, and events

A sealed class is one that cannot be inherited. It is typically used to prevent accidental inheritance of the class. struct are implicitly sealed and thus cannot be inherited. See Sealed Classes in Classes section.

static

Applies To: classes (new in C# 2.0), fields, methods, properties, events, operators, and constructors.

A static type or class member belongs to the class itself rather than to a specific object. Therefore, there is exactly only one copy of each static field in a class. Note the following:

  • A constant is implicitly a static member.
  • A static member cannot be referenced through an instance.
  • You cannot use the this keyword with a static class member.

unsafe

Applies To: Any callable class member (methods, indexer, property, constructor and so on – but not for static constructors)

The unsafe keyword is required for any block of code that uses pointers. Note that the scope of the unsafe keyword extends from the parameter list to the end of the class member. For example:

unsafe public void Square( int *pN )
{
    *pN = (*pN) * (*pN);
}

To compile .NET code that contains an unsafe code you must use the /unsafe compiler option. See Unsafe chapter for more details.

virtual

Applies To: methods, properties, events, and indexers.

The virtual keyword is used to indicate that the implementation of the affected class member depends on the runtime-type of the object being invoked. So when a virtual method is invoked, the runtime-type of the invoked object is checked for an overriding member. The overriding member in the most-derived class is then called.

Note the following points:

  • By default, class members are non-virtual. You cannot override a non-virtual method.
  • The virtual modifier cannot be used on declarations with static, abstract or override.

volatile

Applies To: fields.

The volatile keyword indicates that a filed can be modified in the program by the operating system, hardware, or another concurrently executing thread. A volatile field is usually used for fields that will be accessed by multiple threads without using the lock statement to synchronize access. Using the volatile modifier ensures that one thread retrieves the most-up-to-date value written by another field.