slama.dev

The C# programming language

lecture notes, released on 19. 2. 2021

Preface

This website contains my lecture notes from a lecture by Pavel Ježek from the academic year 2020/2021. If you find something incorrect/unclear, or would like to contribute either text or an image, feel free to submit a pull request (or let me know via email).

Strings

Action Code
split s.Split(char);
split on multiple delimiters s.Split(delimiters);, delimiters is char[]
convert to integer int.Parse(string);, can throw!
length s.Length;

Interning

System.Text.StringBuilder

Interpolation

Console.WriteLine("{0}: Hello, {1}!", s1, s2);

// careful, what if s2 == "{0}"
Console.WriteLine("{0}: Hello, " + s2 + "!", s1, s2);

Chars

File I/O

Action Code
reading System.IO.StreamReader f = new System.IO.StreamReader(path);
read line f.ReadLine();
read chars int chars_read = f.Read(buffer, 0, BUFFER_SIZE);
writing System.IO.StreamWriter f = new System.IO.StreamWriter(path);
write line f.WriteLine(line);
close f.Dispose();

Classes

Constructor

class A {
	int x = stuff;
	
	A () {}
}

// is equivalent to (for each constructor!)
// note that stuff can be arbitrary code

class A {
	A () {
		int x = stuff;
	}
}

When inheriting, the constructor of the predecessor is called like this:

class B : A {
	int x = stuff;
	
	B () {
		stuff2
	}
}

// is equivalent to (for each constructor!)
// note that stuff can be arbitrary code

class B : A {
	B () {
		int x = stuff;
		
		// constructor of A (without parameters)
		
		stuff2
	}
}

If no constructor without parameters exist (for example when a class contains one with parameters, which makes the one without parameters not generate) and we inherit the class without calling it, it won’t compile:

// THIS WON'T COMPILE!

class A {
	public A(int x) { }
}

class B : A {
}

We have to do this instead:

class A : B {
	A () : base(/*parameters for constructor of B*/) {stuff}
}

// is equivalent to (for each constructor!)
// note that stuff can be arbitrary code

class A : B {
	A () {
		int x = something;
		
		// constructor of B (with the appropriate parameters)
		
		stuff
	}
}

Static/class constructor

class A {
	static A() { }
}

Destructors/finalizers

Inheritance

class A { }      // some stuff
class B : A { }  // some stuff + some more stuff

A a = new A(); // is fine
a = new B();   // is also fine
Virtual/abstract/new methods
class A {
	public virtual void f() { Console.WriteLine("A"); }
	public virtual void g() { Console.WriteLine("A"); }
}

class B : A {
	public override void f() { Console.WriteLine("B"); }
	
	// new is optinal, suppresses a warning
	public new void g() { Console.WriteLine("B"); }
}

(new B()).f();  // prints B
(new B()).g();  // prints B

((A)(new B())).f();  // prints B
((A)(new B())).g();  // prints A
class Animal {
	public virtual void Name() { Console.WriteLine("Animal"); }
}

class Mammal: Animal {
	public override void Name() { Console.WriteLine("Mammal"); }
}

class Dog: Mammal {
	// new is optinal, suppresses a warning
	public new virtual void Name() { Console.WriteLine("Dog"); }
}

class Beagle: Dog {
	public override void Name() { Console.WriteLine("Beagle"); }
}

Dog pet = new Beagle();
pet.Name();              // prints Beagle

Animal pet = new Beagle();
pet.Name();              // prints Mammal
  A M D B
A.Name A M M M
D.Name     D B
Superclass to subclass conversion
is
as
B b = a as B;  // assigns `a` to `b` if it's of the valid type
               // this is the reason why `null is A` returns false

if (b != null) {
	// do stuff with `B b`
}

// is almost equivalent to (since C# 7.0)
// only difference is that `b` is not initialized after

if (a is B b) {
	// do stuff with `B b`
}
System.Object
System.ValueType
sealed

Generic classes

class FixedStack<T> {
	T[] a;
	int num;
	
	public FixedStack(int maxSize) {
		a = new T[maxSize];
	}
	
	public void push(T val) {
		a[num] = val;
		num += 1;
	}
	
	public T pop() {
		num -= 1;
		T ret = a[num];
		return ret;
	}
}

Variable scope

Local variables

if <something> {int b;} else {int b;}  // this is ok
int b;  // this is not (already declared in a nested block)
int e = 1, f;
if (e == 1) {f = 2;}
e = f; // error, f is not initialized in all paths

Exceptions

System.Exception

Property/Function Meaning
e.Message string error message
e.StackTrace string trace of method call stack
e.Source string app/object that threw it
e.ToString() string the formatted exception

Syntax

try {
	// stuff
} catch (Exception e) {
	// stuff only executed if the type matches the exception raised
} finally {
	// stuff always executed, even if the exception is not handled
	// for example, for closing IO/network resources
}

Common exception classes


using

Type x;
try {
	x = new Type();  // could raise an exception!
	
	// some code
} finally {
	if (x != null) x.Dispose();
}
using (type x = new Type()) {
	// some code
}
using type x = new Type();

Properties

T Property {
	get { /* stuff to do */ }
	set { /* stuff to do (with a variable `value`) */ }
}

Auto-implemented

Read-only

Instantiating objects with properties

A a = new A { x = 3; y = 6 };

// is literally the same as

A a = new A();
a.x = 3;
a.y = 6;

Parametric properties (indexers)

class File {
	FileStream s;
	
	public int this [int index] {
		get {
			s.Seek(index, SeekOrigin.Begin);
			return s.ReadByte();
		}
		
		set {
			s.Seek(index, SeekOrigin.Begin);
			return s.WriteByte((byte) value);
		}
	}
	
}

Type System

Value types

Simple types

Implicit Conversions.

Nullable types

Reference types

Arrays
Action Code
create int[] array = new int[size];
create statically int[] array = {1, 2, 3};
fill with stuff Array.Fill(table, -1, 0, 256);, requires System
sort Array.Sort(array);, highly optimized
length .Length

(un)boxing

Functional elements

Local functions

static void Main(string[] args) {
	WriteLine("hello");
	
	double arg(int n) {
		return double.Parse(args[n]);
	}
	
	double d = arg(0);
	double e = arg(1);
	WriteLine(d + e);
}

Delegates

using System;

static class Program {
	public delegate bool Condition(int i);
	
	public static void TestNumbers(Condition condition) {
		for (int i = 0; i < 10; i++) {
			if (condition(i))
				Console.WriteLine(i);
		}
	}

	public bool IsEven(int i) => i % 2 == 0;
	public bool IsOdd(int i) => i % 2 == 1;

	static void Main(string[] args) {
		TestNumbers(IsEven);
	}
}

Lambda funtions

Structures

using System;

struct A {
    public int num;
    public int num2;
}

struct B {
    public static A a1;
    public A a2;
}

static class Program {
    static void Main(string[] args) {
        B b = new B();
        Console.WriteLine(B.a1.num);  // is ok
        Console.WriteLine(b.a2.num);  // is also ok

        A a;
        Console.WriteLine(a.num);  // is NOT OK
                                   // we haven't called the constructor

        a.num = 5;
        Console.WriteLine(a.num);  // is ok

        A a2 = a;  // is still NOT OK
                   // the entire struct must be initialized

        a.num2 = 5;

        A a3 = a;  // is ok
    }
}

Visibility

Access Modifiers Access is…
public not restricted.
private limited to the containing type.
protected limited to the containing class derived types.
internal limited to the current assembly.
protected internal limited to the current assembly OR same/derived types.
private protected limited to the current assembly AND same/derived types.
class A {
	private int x;
	
	// is ok, x is private in A
	public int GetX() {
		return x;
	}
	
	// is ALSO OK, since the code is inside A (B : A)
	public static void SetXOnB(B b) {
		b.x = 30;
	}
}

readonly

=>

??, ??=, ?.

Arithmetic overflows

checked {
	// kód
}

References

ref

void Inc(ref int x) { x += 1; }

void f() {
	int val = 3;
	Inc(ref val);  // val will be 4
}

out

void Read(out int first, out int next) {
	first = Console.Read();
	next = Console.Read();
}

void Main() {
	int first, next;
	Read(out first, out next);
}
int a;
if (tryParse(something, out a))
	Console.WriteLine("We failed: " + a);

Console.WriteLine("We didn't fail: " + a);

// is the same as

if (tryParse(something, out int a))
	Console.WriteLine("We failed: " + a);

Console.WriteLine("We didn't fail: " + a);

var

Interfaces

interface Shape {
	double Perimeter();
	double Area();
}

Implementation details

Heaps and garbage collection

Large Object Heap (LOH)
Small Object Heap (SOH)

Terms to know

CIL

CLR

GAC

JIT

AOT

BCL

.NET

Tiered compilation

CLS

typeof(Class)

nameof(x) [Microsoft Docs]

Console.WriteLine(nameof(System.Collections.Generic));  // output: Generic
Console.WriteLine(nameof(List<int>));                   // output: List
Console.WriteLine(nameof(List<int>.Count));             // output: Count
Console.WriteLine(nameof(List<int>.Add));               // output: Add

var numbers = new List<int> { 1, 2, 3 };
Console.WriteLine(nameof(numbers));        // output: numbers
Console.WriteLine(nameof(numbers.Count));  // output: Count
Console.WriteLine(nameof(numbers.Add));    // output: Add

NUnit tests

using System;
using NUnit.Framework;

public class Tests
{
	[SetUp]
	public void Setup() { /* do something for setup (optional) */ }
	
	[Test]
	public void NameOfTheTest()
	{
		Assert.AreEqual("a", "a");
		Assert.AreNotEqual("a", "b");
		Assert.IsTrue(true);
		Assert.IsFalse(false);
		
		Assert.Throws<ArgumentException>(() =>
		{
			This doesn't throw!
		});
	}
}

Data structures

Dictionaries

Action Code
create Dictionary<int, int> d = new Dictionary<int, int>();
contains d.ContainsKey(element);
add d[index] = element;

Queues

Action Code
create Queue<string> q = new Queue<string>();
add q.Enqueue(element);
pop q.Dequeue(element);
size q.Count;
peek q.Peek();

Acknowledgements