Ummary: Const Extern Override Partial (C# 2.0) Readonly Sealed Unsafe Virtual Volatile
Ummary: Const Extern Override Partial (C# 2.0) Readonly Sealed Unsafe Virtual Volatile
SUMMARY
• Introduction
• Access Modifiers
• abstract
• const
• extern
• override
• partial (C# 2.0)
• readonly
• sealed
• unsafe
• virtual
• volatile
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
Meaning
Modifier
public is an access modifier for types
and type members. This is the most
public permissive access level as there are no
restrictions on accessing a public type or
type member.
internal 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.
internal
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.
protected is an access modifier for type
members only. A protected member is
protected only accessible within the body of the
containing type and from within any classes
derived from the containing type.
private private 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.
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:
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.
Allowed
Allowed Default
Default Declared
Context Declared Member
Accessibility Accessibility
Accessibility Accessibility
On Members
namespace public None Internal public
internal
class internal (if top public private public
level) internal protected
internal
internal
protected
private
interface internal (if top public public None
level) internal
struct internal (if top public private public
level) internal internal
private
enum internal (if top public pubic None
level) internal
ABSTRACT
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:
// Non-abstract methods/properties
public long FolderSize
{
get { return 0; }
set { lSize = value; }
}
}
public class WindowsFolder : Folder
{
// Data members
string strFolderName;
CONST
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.
...
}
}
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:
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 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:
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
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 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();
...
}
}
• 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:
• 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
...
[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:
READONLY
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
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:
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:
VIRTUAL
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.
• 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
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.
CLASSES
SUMMARY
• Class Declarations
• Class Members
• Constants
• Fields
• Methods
• Properties
• Events
• Indexers
• Operators
• Constructors
• Destructors
CLASS DECLARATIONS
A class declaration is a type-declaration and takes the following form:
For example:
Abstract Classes
The abstract modifier is used to indicate that the class is incomplete and that it is intended to
be used as a base-class only. Note some of the characteristics of abstract classes:
An abstract class is closely related to an interface in the sense that both declare a 'contract' that
must be adhered to by clients. See interface for more info.
Sealed Classes
A sealed class is that a class that cannot be used as a base class. In other words, it cannot be
derived from; it is at the end of the derivation chain. The sealed keyword is mostly used to
prevent unintended derivation, but it also enables certain runtime optimizations; for example,
because a sealed class is known to never have any derived classes, it is possible to transform
virtual function member invocations on sealed classes into non-virtual invocations (Recall that
virtual member method invocation involves looking up and invoking an entry from a vtable - which
incurs an extra level of indirection.)
Base Classes
Note the following points about base classes:
• If a class declaration has no base class, or if the class only derives from interfaces, then
the direct base class is assumed to be object.
• The set of base classes of a class type is the transitive closure of the direct base class
relationship. In the above example, the base classes of class type B is A and object.
• The direct base class must be at least as accessible as the class type itself:
• The direct base class of a class type must not be any of the following: System.Array,
Sysem.Delegate, System.Enum, or System.ValueType.
• Every class has exactly one direct base class. Except the object class which has no
direct base class and is the ultimate base class of all other classes.
• A class directly depends on its direct base class, and directly depends on a class within
which it is immediately nested (if any). Therefore, the complete set of classes upon
which a class depends is the transitive closure of the "directly depends on" relationships.
Static classes should be used to associate to contain methods that are not associated with a
particular object. Utility classes are often static as they do not require an object instance to
perform their work. The main features of static classes are:
For example, a class to display computer info does not really need to be attached to a specific
instance of the class. You can declare such a class static as follows:
// Constructors
static ComputerInfo()
{ /* Initialize static members to a known state */ }
// Methods
static public string GetComputerName()
{ /* Your implementation */ }
CLASS MEMBERS
C# classes can contain the following categories of members:
• Constants
Represent constant values within the class.
• Fields
Represent variables of the class.
• Methods
Represent computations and actions performed by the class.
• Properties
Represent named characteristics used to read/write those characteristics.
• Events
Represent notifications that can be generated by the class.
• Indexers
Permit instances of classes to be indexed in the same way as arrays.
• Operators
Define which expression operators can be applied to instances of classes.
• Constructors
Instance constructors implement actions required to initialize instances of the class,
where static constructors implement actions required to initialize the class itself (typically
used to initialize static members)
• Destructors
Implement clean-up actions before an instance is permanently discarded from memory.
• Types
Represent types that are local to a class.
// Constants
const int WEIGHT = 1000;
// Constructors
public Vehicle() { ... }
public Vehicle( int nModel ) { ... }
// Methods
void Start() { ... }
// Properties
int Mileage
{
get { ... }
set { ... }
}
// ...
}
Inheritance
Inheritance means that a class implicitly contains all members of its direct base class, except for
instance constructors, destructors, and static constructors of the base class.
A class is permitted to declare a member with the same name or signature as an inherited
member. When this occurs, the derived class member is said to hide the base class member.
Derived class can hide inherited members by declaring new members with the same name or
signature. Note that hiding a member does not remove it - hiding only makes the hidden member
inaccessible form the derived class.
Because only public inheritance is allowed in C#, the is-a relation ship always holds true.
Therefore, if class Student derived from class Person, then public inheritance means that
Student is-a Person, and hence any code that expects an instance of Student can be passed
an instance of Person, because Student is-a Person. This also implies that an instance of
Student can always be treated as instance of Person.
Virtual methods, properties and indexers enable polymorphic behavior wherein actions performed
by the overridden versions of these virtual members will vary depending on the run-time type of
the instance through which the member function is invoked.
/* No inheritance */
public class MyClass { }
/* Single inheritance */
public class MyClass : SomeBaseClass { }
/* No inheritance, but implements two interfaces */
public class MyClass : IEnumerable, ICloneable { }
/* Single inheritance, and implements two interfaces */
public class MyClass : SomeBaseClass, IEnumerable, ICloneable
{ }
Access Modifiers
Class members may have any of five possible kinds of declared accessibility:
• public
• protected
• private (default if none was provided)
• internal
• protected internal
• A static field identifies only one storage location. No matter how many instances of a
class are created, there will always be one copy.
• A static function member does not operate on a specific instance. Therefore, it is a
compile-time error to refer to this in such a function.
Nested Types
A nested type is a type declared within a class or a struct (a non-nested type is one that is
declared within a compilation unit or namespace.)
A nested type declared with a class can have any of the five forms of declared accessibility:
public, protected internal, protected, internal, private, and like other class
members defaults to private. Recall that non-nested types can have public or internal
declared accessibility only. However, a nested type declared within a struct can have only
public, internal, or private declared accessibility, and like other struct members
defaults to private.
A nested type may also use the new modifier to hide a base member of the containing class:
CONSTANTS
A constant is a class member that represents a constant value; i.e., a value that can be computed
at compile-time. A constant declaration takes the following form:
For example:
public class A
{
public const double PI = 3.14157, ZERO = 0.0;
public const double ONE = ZERO + 1;
...
}
Note that a constant with a class is semantically equivalent to a static member in the sense that
each class instance will get the same value for that constant. However, a constant neither
requires nor allows the static keyword to be supplied.
Constants are allowed to depend on other constants within the same program as long as the
dependencies are not of a circular nature:
public class A
{
public const int X = 1;
}
public class B
{
public const int Y = A.X + 1;
}
FIELDS
A field is a class member that represents a variable associated with an object of a class.
Declaring a field takes the following form:
• new
• public
• protected
• internal
• private (default)
• static
• readonly
• volatile
• If a field is not initialized, the initial value of a field whether it is a static field or an instance
field is the default value of the field's type. This initialization happens when the program is
run by having the memory manager initialize field memory to all-bits-zero before it is
allocated for use.
• If a field was initialized, then:
• for static fields this initialization correspond to an assignment statement that is
executed at an implementation-dependent time prior to the static constructor of
the class (if any) and prior to the first use of a static field of that class.
• for instance fields this initialization correspond to an assignment statement that is
executed during class instantiation using any of the instance constructors.
public class A
{
private int m_nIndex; // m_nIndex
defaults to 0
private bool m_bStatus; // m_bStatus
defaults to false
private double m_dX = 1.0, m_dY = 2.0;
private static int m_nX = -1, m_nY = -1;
Readonly fields
Fields declared with readonly keyword are ... read only! Direct assignment to readonly fields
can only occur as follows:
• Non-static fields
These fields can be initialized during declaration or in an instance constructor.
• Static-fields
These fields can be initialized during declaration or in an static constructor.
Note that using an instance constructor (for read-only instance fields) or a static constructor (for
read-only static fields) allows you to pass the read-only field as an out or ref parameter.:
public class C
{
// readonly fields initialized during declaration
private readonly int N = 10;
private static readonly int M = -10;
public C( int o )
{
O = o;
}
static C()
{
P = 10;
}
}
readonly instance fields are specific to each instance. However, static readonly fields are
not specific to any instance as their value will be shared by all instances. static readonly
fields are semantically similar to constants, so when do you use which?
• A static readonly field is useful when a symbolic name for a constant value is
required, but when the type of the value is not permitted in a const declaration.
• A static readonly field is useful when the value cannot be computed at compile-
time.
In the example below, Brush_10, Black, and White cannot be declared as constants because
their values cannot be computed at compile-time:
// Constructors
public MyColor( byte r, byte g, byte b )
{
red = r;
green = g;
blue = b;
}
}
Volatile fields
A volatile field is one that be modified in the program by something such as the operating
system, hardware, or a concurrently executing thread. Access to non-volatile fields in
multithreaded programs can lead to unexpected results due to optimization techniques performed
by the compiler. For volatile fields such optimizations are restricted:
• A read of a volatile field is called a volatile read - it is guaranteed to occur prior to any
references to memory that occur after it in the instruction sequence.
• A write of a volatile field is called a volatile write - it is guaranteed to occur after any
memory references prior to the write instruction in the instruction sequence.
These instructions ensure that all threads will observe volatile writes performed by any other
thread in the order they were performed.
METHODS
A class method is a class member that represents a computation action that can be performed by
an object or class. Declaring a class member takes the following form:
• new
• public
• protected
• internal
• private (default)
• static
• virtual
• sealed
• override
• abstract
• extern
For abstract and extern modifiers, the method body should consist of a semi-colon. For all
others, the method body consist of a block which specifies the statements to be executed. The
signature of a method consists of the method's name and type and kind (value, reference, output)
of each of its format parameters. Note that the signature of a method does not specifically
include the return type not does it include the params modifier that may be specified for the right-
most parameters.
Method Parameters
The format-parameters-list takes the form of one of the following:
• fixed parameters
• fixed parameters, parameter-array
• parameter-array
• Value Parameters.
• Reference Parameters.
• Out Parameters.
• Parameter Arrays.
Value Parameters
Reference Parameters
A parameter declared with the ref keyword is a reference parameter. Unlike a value parameters,
a reference parameter does not create a new local storage location. Instead, a reference
parameter has the same storage location as the variable given as the argument in the method
invocation. This means that any changes to the parameter in the method will be reflected back in
the caller.
Variables representing reference parameters must always be assigned before using them in a
method invocation. The method invocation must also supply the ref keyword for any variable
representing a reference parameter. Also note that an overload will result if two methods differ
only in their use of ref.
class MethodParameterTest
{
public void Swap( ref int x, ref int y)
{
int nTemp = x;
x = y;
y = nTemp;
}
}
MethodParameterTest obMPT = new MethodParameterTest();
int x = 10;
int y = 20;
obMPT.Swap( ref x, ref y ); // x = 20, y = 10
Out Parameters
A parameter declared with the out keyword is output parameter. Similar to a reference
parameters, an output parameter has the same storage location as the variable given as the
argument in the method invocation. This means that any changes to the parameter in the method
will be reflected back in the caller. out parameters are particularly useful when you want a
method to have multiple return values, as any given method can have zero, one, or more out
parameters.
Variables representing out parameters do not need to be assigned before using them in a
method invocation. The method invocation must also supply the out keyword for any variable
representing an output parameter. Within the method, an output parameter is considered
unassigned and must be assigned before its value can be used. Also note that an overload will
result if two methods differ only in their use of out.
class MethodParameterTest
{
public void GetTwoRandomNumbers( out int x, out int y)
{
x = 100; // out parameters must be assigned a
value before function returns
y = 200; // out parameters must be assigned a
value before function returns
}
}
int x;
int y;
obMPT.GetTwoRandomNumbers( out x, out y ); // x = 100; y =
200
Parameter Arrays
A parameter declared with the params keyword is a parameter array. Parameter arrays are used
to pass a variable number of parameters, similar to the ... syntax used in a C++ function. If a
format parameter list includes a parameter array, it must the right-most parameter and it must be
a single-dimensional array. You cannot combine the out or ref modifiers with the params
modifier. The following example illustrates how a declare a method with a params parameter and
how to call it:
class MethodParameterTest
{
public void PassParamterArray( float fNumber, params
string[] aParams )
{
foreach(string s in aParams)
{
Trace.WriteLine( s );
}
}
}
MethodParameterTest obMPT = new MethodParameterTest();
• .NET types can be either value-types or reference-types (ignore the pointer types in
unsafe code as they are irrelevant to this discussion)
• .NET types can either be passed by-value or by-reference.
Therefore, there are four cases for passing method parameters in C#':
• Passing a value type by- value.
• Passing a value type by- reference. Use the ref or out keywords.
• Passing a reference type by- value.
• Passing a reference type by- reference. Use the ref or out keywords.
• Passing any type by-value to a function member means that the the function member will
be working with a copy, and hence changes to the variable will not be seen by the caller.
• Passing any type by-reference to a function member means that the the function member
will be working on the same location as the passed variable, and hence changes to the
variable will be seen by the caller.
• The default for passing value-types is by-value. This means that a copy of the actual data
will be passed and changes to the copy will not be seen by the caller.
• The default for passing reference-types is also by-value. This means that a copy of the
reference will be passed. This further means that changes to the underlying data pointed
to by the reference will be seen by the caller, however, changes to the reference itself,
i.e., re-assigning the parameter to a different memory location works only inside the
method and does not affect the original variable.
• Passing a reference type by-reference means that the actual reference itself will be
passed. This implies two things: Changes to the underlying data will be seen by the
caller, and changes to the reference (i.e., re-allocating the variable) will also be seen by
the caller.
Examples:
Note that a static method does not operate on an instance and hence it is a compile-time error
to use the this keyword inside a static function. However, an instance method operates on a
specific instance and accessing the this keyword is allowed.
Virtual Methods
A virtual method is a method declared with the virtual keyword.
Note the following important differences between virtual and non-virtual method invocation:
• In a virtual method invocation, it is the run-time type of the instance that determines
which method to invoke.
• In a non-virtual method invocation, it is the compile-time type of the instance that
determines which method to invoke.
To help differentiate between compile-time vs. run-time type consider the following:
public class A
{
void A1() { ... }
virtual void A2() { ... }
}
public class B : A
{
override void A2() { ... }
}
When a method declaration contains the override keyword, the method is said to be
overridden. Use the override keyword to override (supercede or specialize) the implementation
of an inherited virtual method. Note that while a virtual method declaration introduces a new
method, an override method declaration specializes an existing inherited virtual method by
providing a new implementation. An override declaration can access the overridden base method
using the base keyword:
...
}
Note that only by introducing the override keyword can a method override another method. In
all other cases, a method with the same signature as an inherited method simply hides the
inherited method. In the example below, Derived1.f() hides Base1.f():
Note also that the sealed keyword can be used when overriding an inherited virtual method to
prevent a derived class from further overriding the method. Note the following code which
extends the pervious example by marking Derived2.f() as a sealed override:
Derived3 attempts to further specialize f() which was inherited from Derived2. However,
Derived2 declared f() as a sealed override, and hence Derived3 attempts to override a
sealed method.
Abstract Methods
An abstract method is a method declared with the abstract modifier. An abstract method is an
implicitly virtual method except that it cannot have the virtual modifier. An abstract method
declaration differs from a virtual method declaration in the following areas:
• An abstract method declaration declaration introduces a new virtual method, but unlike a
virtual method declaration, an abstract method declaration does not provide an
implementation.
• Non-abstract derived classes are required to provide their own implementation by
overriding the inherited abstract method. Whereas a a virtual method declaration does
not require its derived classes to provide their own implementation since the derived
classes inherit the default implementation of the base class virtual method.
Therefore, an abstract method declaration introduces a new virtual function and forces its derived
classes to provide their own implementation. To use abstract methods, they must be declared in
abstract classes and overridden in derived classes:
public abstract class Shape
{
public abstract void Paint(); // Abstract methods have no
body and must be contained in abstract classes
}
The abstract keyword can also be used on a virtual method to force the re-implementation of
the method in derived classes. This can be though as a way to branch the virtual method into a
new implementation. In the following example, class declares A virtual method, class B overrides
this method with an abstract method, and class C then overrides the abstract method to provide
its own implementation. However, class D overrides the method in class A. This results in two
branches:
public class A
{
public virtual void f() { Trace.WriteLine("A.f()"); }
}
public class C : B
{
public override void f() { Trace.WriteLine("C.f()"); } //
overrides the abstract B.f()
}
public class D : A
{
public override void f() { Trace.WriteLine("D.f()"); } //
overrides the virtual A.f()
}
A obC = new C();
obC.f(); // Calls C.f()
External Methods
An external method in C# is a method declared with the extern modifier and implemented
externally perhaps in another language like C++ or VB.NET. Therefore, an external method
declared in C# has no implementation body. The extern modifier is typically used with the
DllImport attribute allowing external methods to be implemented in DLLs (note that when a
method declaration includes a DllImport attribute, the method declaration must also include a
static modifier):
[DllImport("kernel32", setLastError=true)]
static extern bool SetCurrentDirectory( string strCurrent );
// No implementation body (just like abstract methods)
PROPERTIES
A property is a class member that provides access to a characteristic of an object or class.
Examples of properties include size of a window, ID of a student, length of a string and so on.
Properties are a natural extension of fields in that they provide a simple way for getting/setting
property values, and at the same time, provide for a mechanism that
associates actions/computations when getting/setting values.
• new
• public
• protected
• internal
• private (default)
• static
• vritual
• sealed
• override
• abstract
• extern
Even though the syntax for accessing a property is the same as accessing a variable, a property
does not denote a variable. Therefore, it has no memory location and parameter modifiers out
and ref cannot be used to pass properties as function arguments.
Similar to fields and methods, a static property includes the static modifier and is not
associated with a specific instance, while an instance property does not include the static modifier
and is associated with a specific object instance.
Accessors
The accessor-declarations consist of a get-accessor-declaration only for read-only access,
or a set-accessor declaration only for write-only access, or both for read and write access. The
following example shows a very basic and simple property with both a get-accessor-declaration
and set-accessor declaration:
// Properties
string Name
{
get { return m_strName; }
set { m_strName = value; } // 'value' is the implicit
parameter of the set accessor
}
}
Note that for extern and abstract properties, each of its access-declarations should
consist of a semi-colon with no body
When a derived class declares a property by the same name as an inherited property, the derived
property hides the inherited property with respect to both reading and writing:
It is considered as a bad programming practice for get accessors to have observable side
effects. This is because invoking a get accessor is conceptually equivalent to reading a value:
public int X
{
return m_nX++; // Bad programming style. Should
not increment
}
}
This "no-side effect" convention does not mean that the get accessor should always be written to
return values stored in fields. In fact, get accessors often compute the value of a property by
invoking multiple methods and accessing multiple fields. For example, a TransactionCount
property on a data access object can be used to query the database for number of currently
active transactions!
Properties are often used to delay initialization of a field until the moment it is first used:
return reader;
}
}
// Main application
string strLine = Console.In.ReadLine(); // TextReader created
if not already created by a previous call
Finally, note that exposing fields through properties is not any less efficient than exposing fields
directly. In fact, when a property is non-virtual and contains a small amount of code, the execution
environment may replace calls to accessors with the actual code of the accessors. This is known
as in-lining and makes property access as efficient as field access.
// Asymmetric property
public string Name
{
get // The get accessor gets the
accessibility level of the property itself (public)
{
return strName;
}
1. As known from interfaces, you cannot apply access modifiers on interface declaration or
an explicit interface member implementation.
2. You can use asymmetric accessors only if the property or indexer has both set and get
accessors. In this case, the modifier is allowed on only one of the two accessors.
3. If the property or indexer is an override, the accessor modifier must match the accessor
of the overridden accessor, if any.
4. The accessibility level on the accessor must be more restrictive than the accessibility
level on the property or indexer itself.
// Assymetic property
public string Name
{
// The get accessor gets the accessibility level of the
property itself (public).
// If you give the get access an accessibility level,
you get error CS0274: Cannot
// specify accessibility modifiers for both accessors of
the property or indexer
// 'NewFeatures.Accessors.Name'
get
{
return strName;
}
public int ID
{
get
{
return nID;
}
interface IMyInterface
{
// As per interface declaration rules, no access modifier
can be applied to the Name property.
// Also no access modifier can be applied to the get
accessor.
string Name { get; }
}
Another example which illustrates how a property in a derived class can be hidden when using a
more restrictive access modifier:
// Properties
public string Name
{
get { return name; }
set { name = value;}
}
public int ID
{
get { return id; }
set { id = value;}
}
}
// Properties
Output:
• A virtual property is one that is declared with the virtual keyword. The virtual
keyword specifies that the current implementations of the accessors are virtual and that
derived classes are free to specialize them using the override keyword.
• An abstract property on the other hand is one declared with the abstract keyword but
does not provide an actual implementation of the accessors. Because abstract property
declarations are only permitted on abstract classes, it is therefore, a requirement for non-
abstract derived classes to provide their own implementation. This logic is the same as it
is for virtual and abstract methods.
• A property declaration that includes both abstract and override modifiers specifies
that the property is abstract and overrides a base virtual property. This results in a branch
situation which was discussed in the methods section.
• When overriding properties, if the inherited property had a single accessor (i.e., read-only
or write-only), the overriding property must only override that accessor. If the inherited
property included both accessors, the overriding property can override either a single
accessor or both of them.
• An overriding property may include the sealed keyword to indicate that this property
may not be further specialized by derived classes.
For example,
// Properties
public abstract int X
{
get;
}
public class B : A
{
// Overridden properties
public override int X
{
get { /* provide another implementation */ }
set { ... } // error CS0546: 'B.X.set': cannot
override because 'A.X' does not have an overridable set accessor
}
• new
• public
• protected
• internal
• private (default)
• static
• vritual
• sealed
• override
• abstract
• extern
type is the delegate to which you want to associate this event and access-declarators are
used to add and remove event handlers in client code.
When working with events, you typically work with three components - an event, a delegate and
an event handler. An event is fired by a class in certain cases by simply calling the event. A
delegate represents the signature of a callable entity (i.e., a function) that will be called by the
event. An event handle is a client-side function whose signature must match that of the delegate
and that will be called when the event fires.
The event keyword allows you to specify a delegate that will be called upon the occurrence of
some event in your code. The delegate can have one or more methods associated with it that will
be called when the event for that delegate fires. To create C# events:
For example,
Note that just like methods, properties, and fields, an event can either be a static event (not
associated with a specific instance), or it can be an instance event (associated with a specific
instance).
INDEXERS
An indexer is a class member that allows an object to be indexed in the same way as an array.
An indexer is usually added to a class when the class contains an internal collection and you wish
to access this internal collection using array-like syntax. Declaring an indexer takes the following
form:
Note that the static modifier is not permitted. The following shows how to declare and use a
simple indexer:
// Assign values
obDG[0,0] = 10; // OK. Assigns value
obDG[0,1] = 100; // Throws an exception because row !=
column
}
catch( Exception ex)
{
Trace.WriteLine( ex.Message );
}
OPERATORS
An operator is a class member that defines what it means to apply an expression operator to an
instance of a class. For example, if ob1 and ob2 are two objects, what does ob1 + ob2 mean?
Or what does ob1++ mean? The definition of these operators (binary + in the first case, and
unary increment in the second case) is defined by class operators.
• public
• static
• extern
• +
• -
• !
• ~
• ++
• --
• true
• false (true and false require pair-wise declaration - if one is defined the other must
be defined also.)
• +
• -
• *
• %
• /
• &
• |
• and many others (review MSDN).
• An operator declaration must include either extern and hence no function body, or both
public and static modifiers.
• The parameters of an operator must all be value parameters. It is a compile-time error for
an operator declaration to use ref or out parameters.
• It is not possible for an operator declared in a derived class to hide an operator declared
in a base class, Therefore, the new keyword is never permitted on an operator. This is
because operator declarations always require class to participate in the signature of the
operator.
From the above definition that there are three categories of overloadable operators (assume T is
the type of the containing class):
1. Unary operators
1. Operator definition for ++ and -- must take a single parameter of type T and return T.
2. Operator definition for +,-, ! or ~ must take a single parameter of type T and return
any type.
3. Operator definition for true and false must take a single parameter of type T and
return bool.
2. Binary operators
A binary operator declaration must take two parameters one of which must be the type of the
containing class. A binary operator can return any type. Certain binary operators require pair-wise
declaration (if one is defined, then the other must be defined as well:
3. Conversion operators
A conversion operator is used to convert from a source type S to a target type T. The source type
S is indicated by the parameter type of the operator declaration and the target type T is indicated
by the return type of the operator declaration. A class or a struct is allows to declare a
conversion operator if all the following are true:
Note that by eliminating unnecessary casts, implicit conversion can improve source code
readability. However, because implicit conversions can occur without the programmer specifying
them, care must be taken to prevent unpleasant surprises in the form of exception. Therefore,
implicit conversions should never throw exceptions and should never lose information so that they
can be used safely without the programmer worrying about it. If a conversion operator cannot
meet these criteria, then it should be an explicit conversion operator.
Conversion operators are not allowed to redefine a pre-defined conversion. Thus, a conversion
operator is not allowed to convert from / to object because implicit and explicit conversion
operators already exist between object and all other types.
Conversion operators are also not allowed from or to an interface type. This ensures that a
conversion to an interface type succeeds only in object being converted actually implements the
interface type.
The signature of a conversion operator consists of the source type and the target type only (this is
the only class member for which the return type is part of the signature). This also means that
implicit and explicit keywords are not part of the conversion operator signature.
/* Constructors */
public Point()
{
nX = nY = 0;
}
/* Unary Operators */
/* Binary Operators */
// return results
return pt;
}
// return results
return pt;
}
/* Conversion Operators */
CONSTRUCTORS
There are two types of constructors in C#:
• Instance Constructors
• Static Constructors
Instance Constructors
An instance constructor is a class member that implements actions required to initialized an
instance of a class. Declaring a constructor takes the following forms:
• public
• protected
• internal
• private
• extern
Instance constructors are not inherited. Hence, a class has no instance constructors other than
those defined in the class. If a class contains no instance constructor declaration, a default
instructor (one that takes no parameter) is provided automatically.
Constructor Initializers
Note that constructor initializers are only allowed to access the parameters passed to the
instance constructor. The following example illustrates:
public BaseLevel1(int n)
{
Trace.WriteLine("BaseLevel1.BaseLevel1(int n)");
}
}
Trace.WriteLine( "DerivedLevel1.DerivedLevel1(double)");
}
public DerivedLevel1( int n ) : base(n) //
Calls and passes parameter to base class constructor
{
Trace.WriteLine( "DerivedLevel1.DerivedLevel1(int)");
}
public DerivedLevel1( int n, string s ) : this(n) //
Calls and passes parameter to an instance constructor in this
class
{
Trace.WriteLine( "DerivedLevel1.DerivedLevel1(int,
string)");
}
}
// Test2 constructor initializers
Trace.WriteLine( "Calling constructor with no argument" );
DerivedLevel1 ob1 = new DerivedLevel1();
// Main constructor
public Person ( int age, string name )
{
nAge = age;
strName = name;
}
Constructor Execution
Upon entry to the instance constructor and before the implicit invocation of the direct-base class
constructor, variable initializers of the instance fields are executed (in the order in which they
appear in the class). In other words, variable initializers are transformed into assignment
statements and these assignment statements are executed before the invocation of the base
class instance constructor. For example
// Constructors
public Derived( int n ) : base(n)
{
...
}
}
Derived ob = new Derived( 100 );
1. nY is initialized to zero
2. nX is assigned a value of 10 (square root of 100)
3. The base constructor is called.
4. The derived constructor is called.
Private Constructors
For example,
If BaseWithPC constructor was declared protected instead or private, then the object
cannot still be created, but derived classes can derive from it (since the base constructor is now
accessible). In other words:
Static Constructors
Recall that an instance constructor is a class member that implements actions required to
initialized an instance of a class. A static constructor on the other hand is a class member that
implements actions required to initialized a class. In other words, instance constructors initialize
instances and static constructors initialize classes.
The body of a static constructor specified all the statements required to initialize the class. Note
that static constructors are:
• Cannot inherited.
• Cannot be called directly.
• Are parameterless (hence cannot be overloaded.)
• The static constructor of a class executes before any instance of the class is created.
• The static constructor of a class executes before any of the static members of the class
(if any) are referenced.
• The static constructor of a class executes after the static field initializers (if any) for the
class.
• The static constructor of a class executes at most one time during the lifetime of a
program.
DESTRUCTORS
A destructor is class member that implements actions required to destroy an instance of a class.
Declaring a static constructor takes the following forms:
Destructors cannot be invoked directly. It is up to the garbage collector to decide when to invoke
the destructor.
What happens if an exception is thrown and not caught in a destructor? Destructor execution is
terminated and the destructor of the base class is called. If there is no base class, the exception
is discarded.