C# Classes

A class is a construct which defines a type and may contain fields (that is, variables or constants of other types), methods, properties, events, etc. The syntax (rather overwhelming) is:

     access-modifier [static/abstract/sealed] [partial] class class-name [:] [base-class], [interface-name, ...]
     {
     }

That is, we have:

The default access for the class itself, if not specified, is "internal". The default access for its members is "private".

A class can be defined all in one place or it can be defined in parts. To define a class in more than one part, we use the modifier "partial". Each part must include "partial" and all other characteristics must match.


Inheritance

A class may be derived from an existing class called the "base class". The derived class inherits the public, protected and internal members of the base class, and it may add more members of its own. It can also have methods of its own with the same names as methods of the base class; this is called "overriding", and it requires that the base class's method must be virtual or abstract.

Example:

     public class Circle
     {
      public int Radius;
      
      public virtual void Print()   // We need "virtual" to be able to override this later.
      {
       Console.WriteLine("Radius = {0}", Radius);
      }
     }  

     public class ColoredCircle : Circle
     {
      public char Color;

      public override void Print()  // We need "override" here.
      {
       Console.WriteLine("Radius = {0} and Color = {1}", Radius, Color);
      } 
     }

Here ColoredCircle is derived from Circle. It inherits Radius and adds a field of its own, Color.

If we have

     Circle C1;
     ColoredCircle C2;

and later we call their Print() methods, C1.Print() will print C1's Radius value and C2.Print() will print C2's Radius and Color. That is, the correct method will be called.

In this example, Radius and Color are declared as public. It would be a more common practice to declare them as private and provide public properties (get and set) for them.

Notice that a class may be derived from just one base class. C# does not allow multiple inheritance. On the other hand, we could have a third class, FancyCircle which is derived from ColoredCircle.


base

If code in the derived class needs to use the base class's method, it needs a way to refer to it. The keyword "base" is used here. In the above example, code in the ColoredCircle class may need to refer to Circle's Print() method. It could do so with:

     base.Print();

This is unambiguous as we do not have multiple inheritance in C#.

If we had three classes, C derived from B and B derived from A, with a virtual method F in A, overridden in B and then overridden in C, and we wanted to use A's method in an instance of C, we could use casting: recast the instance of C as an instance of A.


Access Modifiers

Access modifiers can be applied to a class, a struct, fields, methods, etc. They are:

Classes and structs declared in a namespace (rather than nested) may be either public or internal.

Derived classes cannot be more accessible than their base types.

Members of structs can be public, internal or private (but not protected as structs do not allow inheritance). The default is private.

Member of classes can be declared with any of the five access modifiers. The default is private. A member of a class normally has access no less restrictive than the class itself. It is worth noticing that some classes have private constructors. (One obtains an instance of such a class as the return value of a method defined elsewhere.)

Derived classes cannot be more accessible than their base classes.

The return type and the parameter types of any member that has such (a method, indexer, or delegate) must be at least as accessible as the member itself.

The following are all public:


this

The term "this" is used in the code of a class to refer to the current instance. For instance, if a method has an argument name matching a member name, we can use "this" to distinguish the member name:

     void public NewSize (int size)
     {
      this.size = size;
     }

Another use might be code in a method to pass the current instance as the argument of a function:

     Update(this);

It makes no sense to use "this" in the methods of a static class.


sealed

If a class definition includes the term "sealed", as in

     sealed public class Blob

then no other class can be derived from it. Structs are inherently sealed.

Some of the predefined classes such as String or SQLComand are sealed.

The modified "sealed" can also be used on methods. Suppose class A is a virtual class and class B derives from A, and suppose class C derives from B. If a method or property in B which overrides a virtual method or property in A is marked "sealed" and "override", then C cannot have a method or property to override it. In this situation, "sealed" must always be used with "override".


static

The modifier "static" can be used with classes, fields, methods, properties, operators, events, and constructors.

A static class can contain only static members.

A member of a class is static, it is not accessed through an instance of the class. Instead, it is accessed using the class name. There is only one copy of each static member. Static members are not accessed using "this".

Example:

     public class Gadget
     {
      static int KKK;
     }

If code needs to refer to KKK, it uses Gadget.KKK.

It is worth noticing that a non-static class or struct can still contain static member. For instance, the Int32 struct has a static method Parse().

A static class is sealed, so no other class can inherit from it, and it cannot itself be derived from another class.

Many of the predefined classes such as Console, Convert or File are static.


partial

The keyword "partial" may apear in the definition of a class, struct or interface. It indicates that the definition may be split over two or more source files. For example (adapted from a MSDN page):

     public partial class Employee
     {
      public void DoWork()
      {
      }
     }

     public partial class Employee
     {
      public void GoToLunch()
      {
      }
     }

This may be useful in work on a large project with several programmers.

When code is automatically generated by the Visual Studio IDE, we will see partial classes.

All parts of the definition must use the keyword "partial" and they must all have the same access modifier (public, private, etc.).


sealed

If a class definition includes the term "sealed", as in

     sealed public class Blob

then no other class can be derived from it. Structs are inherently sealed.

Suppose class A is a virtual class and class B derives from A, and suppose class C derives from B. If a method or property in B which overrides a virtual method or property in A is marked "sealed" and "override", then C cannot have a method or property to override it. In this use, "sealed" must always be used with "override".


override

The modifier "override" is used when a class implements a member (method, property, etc.) it has inherited from a base class. A method that is overridden is called the "overridden base class method", and it must be virtual, abstract or override. The methods must have the same signature and the same access (public, private, etc.).

A method that is static or non-virtual cannot be overridden.

Example (adapted from a MSDN page):

     abstract class ShapesClass // This is the base class.
     {
      abstract public int Area();
     } 

     class Square : ShapesClass // This is the derived class.
     {
      int side = 0;

      public Square(int n) // constructor
      {
       side = n;
      }

      // Area method is required to avoid a compile-time error.
      public override int Area()
      {
       return side * side;
      }

      static void Main() 
      {
       Square sq = new Square(12);
       Console.WriteLine("Area of the square = {0}", sq.Area());
      }

     // Output: Area of the square = 144


abstract

The keyword "abstract" indicates that something has a missing or incomplete implementation. An abstract class is intended to be used only as a base class for deriving other classes. An abstract member of a class or a member included in an abstract class must be implemented by any derived class. (There must be an implementation somewhere.)

See the example listed for "override". The Area() method must be implemented because it is inherited from the abstract class ShapesClass.

An abstract class cannot be instantiated. It may contain abstract members. It cannot be sealed. ("Sealed" and "abstract" are opposite concepts.)

Abstract methods are implicitly virtual methods, and they are permitted only in abstract classes. An abstract method has no method body; its declaration ends with a semicolon and no braces, as in:

     public abstract void F();

An abstract method cannot be marked "static" or "virtual".

Abstract properties behave much like abstract methods. A static property cannot be marked as abstract.

If an abstract class implements an interface, it must provide an implementation for each interface member, as in this example (from an MSDN page):

     interface I
     {
      void M();
     }

     abstract class C : I
     {
      public abstract void M();
     }

Of course, a class derived from C must also implement I and have a method M().


virtual

The keyword "virtual" is used to mark a member of a class to allow it to be overridden in a derived class. By default, methods are non-virtual and cannot be overridden. Classes themselves cannot be declared as virtual.

If a class has a virtual method, it may provide a default implementation for that method, so the derived class does not necessarily have to provide the implementation. When a virtual method is called, the run-time type of the object is checked and the overriding member in the most derived class is used (which might be the original member).

Virtual properties behave much like abstract methods. A static property cannot be marked as virtual.

The distinction between "abstract" and "virtual" is that an abstract member must be overridden and a virtual member may be overridden. Thus "abstract" is a stronger condition.

Here is a diagram of how some of these terms work together:

     +-------------------------+---+--------+--------+--------+----------+
     |       Class Type        |   | normal | static | sealed | abstract |
     +-------------------------+---+--------+--------+--------+----------+
     | Can be instantiated     | : | YES    | NO     | YES    | NO       |
     | Can be inherited        | : | YES    | NO     | NO     | YES      |
     | Can inherit from others | : | YES    | NO     | YES    | YES      |
     +-------------------------+---+--------+--------+--------+----------+