Revision

Back to C#


Namespace

In C# there is no import of file. Import of code is done using namespaces.

Import a namespace using:

  using System;
  using System.Collections.Generic;
  using MyNamespace;

Create a namespace using:

  MyNamespace {
    // Code of my MyNamespace
  }

You can use the same namespace in different files. Every code block inside the same namespace can access information from this namespace from other files:

In file1.cs:

  MyNamespace {
    class MyClassFile1{
      // Code of my MyClassFile1
    }
  }

In file2.cs:

  MyNamespace {
    class MyClassFile2{
      mcf1 = new MyClassFile1();
    }
  }


Console print

Print in console using:

  Console.WriteLine("Hello World"); // Add breakline after text
  Console.Write("Hello World"); // Does not add breakline after text

You can write variable values using {} and using a $ at the beginning of the string:

  int i = 1, j = 2;
  Console.WriteLine($"{i.ToString()} is smaller than {j.ToString()}");

ToString() is used to cast variable to string.


Ternary Operator

A Ternary Operator syntax is condition ? statement 1 : statement 2.
It returns statement 1 if condition is true and statement 2 otherwise.

  int x = 20, y = 10;
  var result = x > y ? "x is greater than y" : "x is less than y";
  Console.WriteLine(result); // x is greater than y


Switch

The switch statement can be used instead of if else statement when you want to test a variable against three or more conditions.

  int x = 10;

  switch (x) {
    case 5:
        Console.WriteLine("Value of x is 5");
        break;
    case 10:
        Console.WriteLine("Value of x is 10");
        break;
    case 15:
        Console.WriteLine("Value of x is 15");
        break;
    default:
        Console.WriteLine("Unknown value");
        break;
  }
  // Value of x is 10


Loop

For loop

  for(int i = 0; i < 10; i++) {
    //code block
  }


while loop

  while (i < 10) {
    //code block
    i++;
  }


do while loop

The do while loop is the same as while loop except that it executes the code block at least once.

  do {
    //code block
    i++;
  } while (i < 5);


foreach loop

  char[] charArray = {'H','e','l','l','o'};
  foreach(char c in charArray) {
    //code block
  }


try - catch - finally

See also this Microsoft C# documentation page on exceptions handling.

A try block is used by C# programmers to partition code that might be affected by an exception.
The catch block deals with exceptional circumstances (errors).
The finally block is used to release the resources regradless that an error occured or no.


Example

  using System.IO

  void ReadFile(string path) {
    try {
      reader = new StreamReader(path);
      string? line = reader.ReadLine();
      while (line != null)
      {
          Console.WriteLine(line);
          line = reader.ReadLine();
      }
    }
    catch (Exception e) {
      Console.WriteLine($"Error: {e.Message}");
    }
    finally {
      // if reader could open the file but error occurs after
      if (reader != null)
        reader.Close();
    }
  }


Enum

Enum is a simple data structure used to declare a set of named constants. Enum is based on types byte or sbyte or short or ushort or int or uint or long or ulong or int.
An enum declaration that does not explicitly declare an underlying type has an underlying type of int.


Creation

Basic Enum creation
enum Color {
    Red, // 0
    Green, // 1
    Blue // 2
}

Console.WriteLine(Color.Green); // Green
Console.WriteLine((int) Color.Green); // 1


Other Enum creation
enum Color: long {
    Red = -1,
    Green = 5,
    Blue = 8
}

Assigned values does not require to be unique. This is valid:

enum Color: long {
    Red = -1,
    Green = 5,
    Blue = 5
}


Mutable and Immutable

Mutable and immutable are English words that mean “can change” and “cannot change” respectively. The meaning of these words is the same in C# programming language; that means the mutable types are those whose data members can be changed after the instance is created but Immutable types are those whose data members can not be changed after the instance is created.


Both structs and classes can be either mutable or immutable. All four combinations are possible. If a struct or class has non-readonly public fields, public properties with setters, or methods which set private fields, it is mutable because you can change its state without creating a new instance of that type.


Multitasking

Multi-Threading vs Multi-Processing

From this Toward Data Science blog post.


Multitasking is the simultaneous execution of multiple tasks or processes over a certain time interval. Multithreading and multiprocessing are two ways to achieve multitasking.


Multithreading refers to the ability of a processor to execute multiple threads concurrently, where each thread runs a process. Whereas multiprocessing refers to the ability of a system to run multiple processors concurrently, where each processor can run one or more threads.


Multi-Threading

Multithreading is a way to achieve multitasking by executing multiple threads concurrently on a processor, where each thread runs a process.


Examples

Example specifying different thread

Example from Geeks for Geeks:

  using System;
  using System.Threading;

  public class GFG
  {
    public static void method1() {
      for (int  = 0; i <= 5; i++) {
        Console.WriteLine("Method1 is : {0}", i);
            if (i == 3)
                Thread.Sleep(6000); // 6s
        }
    }

    public static void method2() {
        for (int j = 0; j <= 5; j++)
            Console.WriteLine("Method2 is : {0}", j);
    }

    static public void Main() {
      Thread thr1 = new Thread(method1);
      Thread thr2 = new Thread(method2);
      thr1.Start();
      thr2.Start();
    }
  }

The output will look like something like this (Method2 does not wait Method1 to finish for starting):

  Method1 is : 0
  Method1 is : 1
  Method2 is : 0
  Method1 is : 2
  Method1 is : 3
  Method2 is : 1
  Method2 is : 2
  Method2 is : 3
  Method2 is : 4
  Method2 is : 5
  Method1 is : 4
  Method1 is : 5


Example using Parallel.For

Parallel.For syntax is Parallel.For(from, to, function). Parallel.For will apply on multiple threads the function function for every values between from and to (exclude).

It is equivalent to the classic

  for (i=from; i < to; i++)
    function(i);

Example from Dot Net Tutorials:

  using System.Threading.Tasks;

  Parallel.For(1, 11, number => {
    Console.WriteLine(number); // 1 2 3 4 5 6 7 8 10 9
  });


Example using Parallel.ForEach

Parallel.For syntax is Parallel.For(from, to, function). Parallel.For will apply on multiple threads the function function for every values between from and to (exclude).

Example from Dot Net Tutorials:

  using System.Collections.Generic;
  using System.Threading.Tasks;

  List<int> integerList = Enumerable.Range(1, 10).ToList();
  Parallel.ForEach(integerList, i =>
  {
      Console.WriteLine("{0}", i); // 3 7 4 8 5 1 6 9 2 10
  });


Multi-Processing

C# does not really have Multiprocessing feature (from this StackOverflow post).


Boxing and Unboxing

Boxing

Boxing converts a Value Type variable into a Reference Type variable that has the type object or to any interface type implemented by this value type.

  int i = 123;

  object o = i; // boxing  -> reference type
  object p = (object)i; // -> reference type - explicit boxing  also valid

The result of this statement is creating an object reference o, on the stack, that references a value of the type int, on the heap.


Unboxing

Unboxing converts a Reference Type variable into a Value Type variable by extracting the value from the object.

  int i = 123;      // a value type
  object o = i;     // boxing -> reference type
  int j = (int)o;   // unboxing -> value type

The result of this statement is creating an int value type j, on the stack, that has the value of the references of the object o.


Boxing and Unboxing Class Examples

  public class Cache { }
  public class DiskCache : Cache { }
  public class MemoryCache : Cache { }
  public class OptimizedDiskCache : DiskCache { }

  /**** Valid ****/
  /** 1 **/
  OptimizedDiskCache optimizedDiskCache = new OptimizedDiskCache();
  Cache cache = (Cache) optimizedDiskCache; // Boxing - Cache is parent of OptimizedDiskCache

  /** 2 **/
  OptimizedDiskCache optimizedDiskCache = new OptimizedDiskCache();
  DiskCache diskCache = (DiskCache) optimizedDiskCache; // Boxing - DiskCache is parent of OptimizedDiskCache

  /** 3 **/
  OptimizedDiskCache optimizedDiskCache = new OptimizedDiskCache();
  Cache cache = (Cache) optimizedDiskCache; // Boxing - Cache is parent of OptimizedDiskCache
  DiskCache diskCache = (DiskCache) cache; // Unboxing - DiskCache child of Cache and cache boxed reference type child of DiskCache object


  /**** Not Valid ****/
  /** 1 **/
  MemoryCache memoryCache = new MemoryCache();
  Cache cache = (Cache) memoryCache; // OK
  DiskCache diskCache = (DiskCache) cache; // ERROR - DiskCache child of Cache, cache boxed reference of MemoryCache object but MemoryCache not DiskCache (or children)

  /** 2 **/
  DiskCache diskCache = new DiskCache();
  OptimizedDiskCache optimizedDiskCache = (OptimizedDiskCache) diskCache; // ERROR - Not a boxed reference of OptimizedDiskCache (or children) -> can't unbox

  /** 3 **/
  Cache cache = new Cache();
  MemoryCache memoryCache = (MemoryCache) cache; // ERROR - Not a boxed reference of MemoryCache (or children) -> can't unbox


Generic type parameters

A generic type parameter allows you to specify an arbitrary type T to a method at compile-time, without specifying a concrete type in the method or class declaration.


Generic type class

Generic type Built-in class

List, SortedList, HashSet, Dictionary, Tuple, Queue and Stack all used generic type parameters.
List is List<T>, Dictionary is Dictionary<TKey, TValue>, etc.


Generic type class

  public class GenericList<T> {
    public void Add(T input) { } // Here do nothing but can be used to store input
  }

  GenericList<int> list1 = new GenericList<int>();
  list1.Add(1);

  GenericList<string> list2 = new GenericList<string>();
  list2.Add("");


Generic type methods

Generic type Built-in methods

The built-in Compare method use generic type.

  public abstract int Compare (T? x, T? y); // return "< 0" if x<y, "= 0" if x=y and "> 0" if x>y


Example of Compare override

  public class Box
  {
      public double Length;
      public double Height;
      public double Width;

      public Box(double l, double h, double w) {
          Length = l; Height = h; Width = w;
      }

  }

  public class BoxCompare : Comparer<Box>
  {
      public override int Compare(Box x, Box y) {
          if (x.Length * x.Height * x.Width > y.Length * y.Height * y.Width)
              return (1);
          else if (x.Length * x.Height * x.Width < y.Length * y.Height * y.Width)
              return (-1);
          else
              return (0);
      }
  }

  Box box1 = new Box(1, 1, 1);
  Box box2 = new Box(1, 2, 1);
  BoxCompare boxCompare = new BoxCompare();
  Console.WriteLine(boxCompare.Compare(box1, box2)); // -1


Generic type methods

  static void Swap<T>(ref T lhs, ref T rhs) {
      T temp;
      temp = lhs;
      lhs = rhs;
      rhs = temp;
  }

  int a = 1;
  int b = 2;
  Swap<int>(ref a, ref b); // Swap(ref a, ref b) also works
  Console.WriteLine(a + " " + b); // 2 1


Indexers

Indexers allow instances of a class or struct to be indexed just like arrays. The indexed value can be set or retrieved without explicitly specifying a type or instance member.

  class SampleCollection<T> {
    private T[] arr = new T[100];

    public T this[int i] {
      get { return arr[i]; }
      set { arr[i] = value; }
   }
 }

 var stringCollection = new SampleCollection<string>();
 stringCollection[0] = "First line";
 stringCollection[1] = "Second line";

 Console.WriteLine(stringCollection[0]); // First line
 Console.WriteLine(stringCollection[1]); // Second line


Delegate

A delegate is a type that represents references to methods with a particular parameter list and return type.
In other word, a delegate represents a template (signature and return type) that a function called by another function must respect.


Example from Windows page on Delegate examples:

  public delegate void Del(string message); // delegate
  public static void DelegateMethod(string message) { Console.WriteLine(message); } // function that respects delegate template

  public static void MethodWithCallback(int param1, int param2, Del callback) {
    callback("The number is: " + (param1 + param2).ToString());
  }

  MethodWithCallback(1, 2, DelegateMethod); // The number is: 3

  // Alternative
  Del handler = DelegateMethod;
  MethodWithCallback(1, 2, handler); // The number is: 3


Func

Func is a generic delegate included in the System namespace. It has zero or more input parameters and one out parameter. The last parameter specified in the brackets “<>” is the out parameter.


Examples

Basic Example
  static int Sum(int x, int y) { return x + y; }

  Func<int,int, int> add = Sum; // Assign method to delegate - Last int represents the return type

  int result = add(10, 10);
  Console.WriteLine(result);


Example of integral computation

Compute numerically the integral of any function is done by calling a function integral that take in argument the mathematical function we want to compute its integral. This function used as parameter is in C# a delegate that can be represente using Func.

  // Integrate takes in input a method that takes a double in input and returns a double
  double Integrate(Func<double, double> f, double x_low, double x_high, int N_steps) {
      double h = (x_high - x_low) / N_steps;
      double res = (f(x_low) + f(x_high)) / 2;

      for (int i = 1; i < N_steps; i++)
          res += f(x_low + i * h);

      return (h * res);
  }

  double f(double x) => x;
  Console.WriteLine(Integrate(f, 0, 10, 100)); // 50

  double f(double x) => x*x;
  Console.WriteLine(Integrate(f, 0, 10, 100)); // 333.35 (true value : 333.33)


Integrate function from this StackOverflow post.


Action

Action is a delegate type defined in the System namespace. An Action type delegate is the same as Func delegate except that the Action delegate doesn’t return a value. In other words, an Action delegate can be used with a method that has a void return type.


Example

  static void ConsolePrint(int i) { Console.WriteLine(i); }

  Action<int> printActionDel = ConsolePrint;
  printActionDel(10); // 10


Predicate

Predicate represents a method containing a set of criteria and checks whether the passed parameter meets those criteria. A predicate delegate methods must take one input parameter and return a boolean - true or false.


Example

  Predicate<string> isUpper = s => s.Equals(s.ToUpper()); // Predicate checks the condition s = s.ToUpper()
  bool result = isUpper("hello world!!"); // false


Ressources

See: