Revision

Back to C# - Class, Struct and Interface


Class

A type that is defined as a class is a reference type. Classes are creatd using the class keyword.


Creation

A simple class is created like this:

  class MyClass
  {
    // Code of your class
  }

It generally contains a creator that will specify how to create the instance of the class and some variable:

  class MyClass
  {
    private int myClassVariable;

    public MyClass(){
      // Code of you Class Creator
    }

    public void myClassMethod() {}

  }

Fields (also called attributes or variables) and methods inside classes are often referred to as members:


Creation with inheritance

If the class inherit from another class or interface (a class can inherit from only on parent class but from multiple interfaces) the class creation is a little bit more complex:

  interface IHobby
  {
      public string Hobby { get; set; }
  }

  interface IHaircut
  {
      public string HairColor { get; set; }
      public void printHairColor();
  }

  class Person
  {
      public int age;
      public void printAge() { Console.WriteLine($"Person age is: {age}"); }
  }

  // A Footballer is a personne with a haircut and hobby
  class Footballer: Person, IHaircut, IHobby
  {
      public string HairColor { get; set; } // A class must implement all members of interfaces from which it derived
      public string Hobby { get; set; } // A class must implement all members of interfaces from which it derived

      public Footballer(int age, string hairColor, string hobby)
      {
          this.age = age;
          this.HairColor = hairColor;
          this.Hobby = hobby;
      }

      public void printHairColor() { Console.WriteLine($"Hair Color is: {HairColor}"); }
  }


Instanciation

  class MyClass { }
  MyClass myClass = new MyClass();


All the following instanciations methods also work:

  class MyClass
  {
    public int myInt;
    public string myString;

    public MyClass() { }
    public MyClass(int varInt) { myInt = varInt; }
    public MyClass(string varString) { myString = varString; }
    public MyClass(int varInt, string varString) { myInt = varInt; myString = varString; }
  }

  MyClass myClass11 = new MyClass();
  MyClass myClass12 = new MyClass(1);
  MyClass myClass13 = new MyClass("text");
  MyClass myClass14 = new MyClass(1, "text");

  MyClass myClass21 = new MyClass() { myInt = 1 , myString = "text"};
  MyClass myClass22 = new MyClass(1) { myString = "text"};
  MyClass myClass23 = new MyClass("text") { myInt = 1 };
  MyClass myClass24 = new MyClass(1, "text");


Types of inheritance

Non Virtual, Non Abstract

Non virtual or abstract methods can’t be overriden.

  class Person
  {
      public int age;

      public Person(int age) {
          this.age = age;
      }

      public void printAge() { Console.WriteLine($"Person age is: {age}"); }
  }

  class Footballer: Person
  {
      public Footballer(int age) : base(age) { }
      // Can't override printAge function
  }

  Footballer footballer = new Footballer(30);
  footballer.printAge() // Person age is: 30


Virtual

virtual methods from parents class may be overriden using the override keyword.

  class Person
  {
      public int age;

      public Person(int age) {
          this.age = age;
      }

      public virtual void printAge() { Console.WriteLine($"Person age is: {age}"); }
  }

  class Footballer: Person
  {
      public Footballer(int age) : base(age) { }

      // Not mandatory to override the function
      public override void printAge() { Console.WriteLine($"Footballer age is: {age}"); }
  }

  Footballer footballer = new Footballer(30);
  footballer.printAge() // Footballer age is: 30


Abstract

abstract methods from abstract parents class must be overriden using the override keyword. A non abstract class can’t have abstract (ie undefined) methods.

  abstract class Person
  {
      public int age;
      public abstract void printAge();
  }

  class Footballer: Person
  {
      public Footballer(int age) {
          this.age = age;
      }

      public override void printAge() { Console.WriteLine($"Footballer age is: {age}"); }
  }

  Footballer footballer = new Footballer(30);
  footballer.printAge() // Footballer age is: 30


Class Types

There exist different types of class in C#. The basic behaviour of


Basic

  class MyClass { }


Static Class

A Static Class cannot instantiate objects with the new keyword, and class members can only be accessed by using the actual class name.

  static class MyClass {
    static public int myClassStaticVariable = 1;
  }
  Console.WriteLine(MyClass.myClassStaticVariable); // 1

Every member of a static class must also be declared as static.


Abstract class

Abstract class cannot be used to create objects directly. An abstract class is just a template for its children classes. Children classes of an abstract class can be instantiate.

  abstract class MyClass
  {
    public int myClassVariable = 1;
    public void printVariable() { Console.WriteLine(myClassStaticVariable); }
    public abstract void printVariableSquared();
  }

  class MyChildClass: MyClass
  {
    public override void printVariableSquared() {Console.WriteLine(myClassStaticVariable * myClassStaticVariable)}
  }

  MyChildClass myChildClassInstance = new MyChildClass();
  Console.WriteLine(myChildClassInstance.myClassVariable); // 1


Partial Class

Partial Class allows methods, events, and properties to be split into separate/multiple .cs source files, which are combined into one class at compile time.


In file1.cs:

  partial class MyClass {
    public int myClassVariable1 = 1;
  }


In file2.cs:

  partial class MyClass {
    public int myClassVariable2 = 2;
  }


Sealed Class

Sealed classes are used to restrict the inheritance feature of object-oriented programming. Once a class is defined as a sealed class, this class cannot be inherited.

  sealed class MySealedClass {}


Access modifiers

Access modifiers are keywords that modify the rights to access class, attributes and methods.
Each of these keywords may be applied to the class itself, to its attributes or to its methods.


Modifiers for attributes and methods

The default accessibility of class members in C# is private (see Microsoft page on accessibility levels).


Public

Public attributes and methods are accessible from anywhere in the code.

  class MyClass {
    public int publicAttribute;
    public void publicMethod() {}
  }


Private

Private attributes and methods are only accessible from within the class.

  class MyClass {
    private int privateAttribute;
    private void privateMethod() {}
  }


Protected

Protected attributes and methods are only accessible by class members and classes that inherit from it.

  class MyClass {
    protected int privateAttribute;
    protected void privateMethod() {}
  }


Internal

Internal attributes and methods are only accessible at the current assembly point of the class.

  class MyClass {
    internal int privateAttribute;
    internal void privateMethod() {}
  }


Modifiers for class

The default accessibility of a class is internal. A class can’t be declared as private.


Public

A public class is accessible from anywhere in the code.

  public class MyClass { }


Protected

A protected class is only accessible by class members and classes that inherit from it. It is not used a lot but here is an example (example from StackOverflow):

  class MyParentClass {
    protected class ProtectedClass { }
  }

  class MyChildClass: MyParentClass {
    private void methodCallingProtectedClass(Outer.Foo foo) { } // OK
  }

  class MyOtherClass {
    private void methodCallingProtectedClass(Outer.Foo foo) { } // ERROR: Access error
  }

Here, the ProtectedClass is accessible from MyChildClass as MyChildClass inherits (derives) from MyParentClass but it is not accessible from MyOtherClass.


Internal

An internal class is only accessible at the current assembly point of the class.

  internal class MyClass { }


Property, Accessor and get/set

A property is a member that provides a flexible mechanism to read, write, or compute the value of a private field. Properties can be used as if they’re public data members, but they’re special methods called accessors. This feature enables data to be accessed easily and still helps promote the safety and flexibility of methods. (from Microsoft page on C# properties).


Properties can be read-write, readable only or writable only (this one is less common).
get and set functions are used to respectively read and write data through the Property. The get and set are accessed transparantly by just calling the Property.


Example

Basic Property

  public class MyClass {
    public string Name { get; set; } // Basic get and set: returns the value of Name and write to the value of Name
  }
  MyClass myClass = new MyClass();
  myClass.Name = "Henry";
  Console.WriteLine(myClass.Name); // Henry


Basic Property: Other Syntax

  public class MyClass {
    private string _name;

    public MyClass(string name) {
        _name = name;
    }

    public string Name {
        get => _name;
        set => _name = value;
    }
  }
  MyClass myClass = new MyClass("John");
  Console.WriteLine(myClass.Name); // John
  myClass.Name = "Henry";
  Console.WriteLine(myClass.Name); // Henry


More Complex Property

public class TimePeriod {
  private double _seconds; // can't access this field outside the class, must use Hours

  public double Hours {
      get { return _seconds / 3600; }
      set {
          if (value < 0 || value > 24)
              throw new ArgumentOutOfRangeException(nameof(value), "The valid range is between 0 and 24.");
          _seconds = value * 3600;
      }
  }
  TimePeriod timePeriod = new TimePeriod();
  timePeriod.Hours = 5; // transform and store data in _seconds
  Console.WriteLine(timePeriod.Hours); // 5: retrieve data from _seconds and transform it in hours
}


Required

  public class MyClass {
    public required string Name { get; set; }
  }
  MyClass myClass = new MyClass { Name = "Henry" };
  Console.WriteLine(myClass.Name); // Henry


Implicit and Explicit inheritance from Interface

When inheriting from an interface, the override of the method may be implicit or explicit.


Implicit override

Implicit override is the classic way of inheriting an interface method.

  interface ISampleInterface {
    void SampleMethod();
  }

  class ImplementationClass : ISampleInterface {
    public void SampleMethod() {
        Console.WriteLine("Implementation of interface method.");
    }
  }

  /* You can instanciate an object of class ImplementationClass and call the method SampleMethod */
  ImplementationClass obj = new ImplementationClass();
  obj.SampleMethod(); // "Implementation of interface method."


Explicit override

  interface ISampleInterface {
    void SampleMethod();
  }

  class ImplementationClass : ISampleInterface {
    void ISampleInterface.SampleMethod() { Console.WriteLine("Implementation of interface method."); } // Should not reuse public keywork
  }

  /* You can't instanciate an object of class ImplementationClass using ImplementationClass constructor and call the method SampleMethod */
  /* You need to instanciate a ISampleInterface object using ImplementationClass constructor to call SampleMethod */
  ISampleInterface obj = new ImplementationClass();
  obj.SampleMethod(); // "Implementation of interface method."


Boxing and Unboxing

See Boxing and Unboxing Class Examples.


Ressources

See: