Getting Started

What’s CSharp?

C# is a high-level, general-purpose programming language developed by Microsoft as part of the .NET framework. It’s an object-oriented language, meaning it uses objects to structure code and data, and is used to build a variety of applications. C# is known for its ease of learning, strong community support, and ability to produce highly performant code.

.NET SDK Instalation

The easiest way to have the .NET SDK installed in your personal computer is to download Visual Studio. You can also use other IDE, but you will need to install the SDK manually.

Hello World

A sample C# program is show here.

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

Run the program as below:

$  dotnet run Program.cs
Variables

Normal Declaration:

string firstName = "Someone";
char userOption = 'A';
int gameScore = 123;
float percentage = 12.10;
double portion = 4.556
decimal particlesPerMillion = 123.4567;
bool processedCustomer = true;

Implicitly Typed:

var message = "Hello world!";
Constant
const int ConstNum = 5;
Data Types

There are two different data types in C#:

  • Value Types: Directly store the data. Once you assign a value, it holds that data
    • int, char, float are just a few examples.
  • Reference Types: Store a memory address. They point to the address of the value.
    • string, class, array are commonly used.
Integer

Math operations:

int sum = 7 + 5;
int difference = 7 - 5;
int product = 7 * 5;
int quotient = 7 / 5;
int modulus = 7 % 5;

Console.WriteLine("Sum: " + sum); // Sum: 12
Console.WriteLine("Difference: " + difference); // Difference: 2
Console.WriteLine("Product: " + product); // Product: 35
Console.WriteLine("Quotient: " + quotient); // Quotient: 1
Console.WriteLine($"Modulus: {7 % 5}"); // Modulus: 2

Order of operations

In math, PEMDAS is an acronym that helps students remember the order of operations. The order is:

  1. Parentheses (whatever is inside the parenthesis is performed first)
  2. Exponents
  3. Multiplication and Division (from left to right)
  4. Addition and Subtraction (from left to right)

Increment and decrement

int value = 1;

value = value + 1;
Console.WriteLine("First increment: " + value); // First increment: 2

value += 1;
Console.WriteLine("Second increment: " + value); // Second increment: 3

value++;
Console.WriteLine("Third increment: " + value); // Third increment: 4

value = value - 1;
Console.WriteLine("First decrement: " + value); // First decrement: 3

value -= 1;
Console.WriteLine("Second decrement: " + value); // Second decrement: 2

value--;
Console.WriteLine("Third decrement: " + value); // Third decrement: 1
String

Combine String using character escape sequences:

// Character escape sequences
Console.WriteLine("Hello\nWorld!");
Console.WriteLine("Hello\tWorld!");
Console.WriteLine("Hello \"World\"!"); // Hello "World"!
Console.WriteLine("c:\\source\\repos"); // c:\source\repos

// Verbatim string literal
Console.WriteLine(@"    c:\source\repos    
        (this is where your code goes)");

//    c:\source\repos    
//        (this is where your code goes)

// Unicode escape character
Console.WriteLine("\u3053\u3093\u306B\u3061\u306F World!"); // こんにちは World!

Combine String using string concatenation:

string firstName = "Bob";
string greeting = "Hello";
string message = greeting + " " + firstName + "!";
Console.WriteLine(message); // Hello Bob!

Combine String using string interpolation:

string firstName = "Bob";
string greeting = "Hello";
Console.WriteLine($"{greeting} {firstName}!"); // Hello Bob!

// Combine verbatim literals and string interpolation
string projectName = "First-Project";
Console.WriteLine($@"C:\Output\{projectName}\Data"); // C:\Output\First-Project\Data
Array

Declaration:

string[] customerIds = new string[3];
string[] customerIds = [ "A123", "B456", "C789" ]; // Introduced in C#12
string[] customerIds = { "A123", "B456", "C789" }; // Older version

Assigning values:

string[] customerIds = new string[3];

customerIds[0] = "C123";
customerIds[1] = "C456";
customerIds[2] = "C789";

Size of the array:

string[] customerIds = [ "A123", "B456", "C789" ];
Console.WriteLine($"There are {customerIds.Length} customers.");
Tuples

Declaration:

var pt = (X: 1, Y: 2);

var slope = (double)pt.Y / (double)pt.X;
Console.WriteLine($"A line from the origin to the point {pt} has a slope of {slope}."); // A line from the origin to the point (1, 2) has a slope of 2.
Methods

No params:

Console.WriteLine("Generating random numbers:");
DisplayRandomNumbers(); // 17 29 46 36 3 

void DisplayRandomNumbers() 
{
    Random random = new Random();

    for (int i = 0; i < 5; i++) 
    {
        Console.Write($"{random.Next(1, 100)} ");
    }

    Console.WriteLine();
}

Using parameters:

CountTo(5);

void CountTo(int max) 
{
  for (int i = 0; i < max; i++)
  {
    Console.Write($"{i}, "); // 0, 1, 2, 3, 4
  }
}

Optional parameters:

CountTo();

void CountTo(int max = 10) 
{
  for (int i = 0; i < max; i++)
  {
    Console.Write($"{i}, "); // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  }
}

Returning values:

int sum = SumTo(5);
Console.Write($"sum: {sum}"); // sum: 15

int CountTo(int max) 
{
  int result = 0;

  for (int i = 1; i <= max; i++)
  {
    result += i;
  }

  return result;
}
Stateless

The following code is stateless because it doesn’t require to store any state to work, you just call the static method WriteLine from Console class.

Console.WriteLine("Hello World!");
Stateful

The following code is stateful because it is required to store previous information of the state to calculate next random value.

Random dice = new Random();
int roll = dice.Next(1, 7);
Condition

if-else operator

string color = "black";

if (color == "black")
{
  Console.WriteLine("It's black.");
}
else if (color == "white")
{
  Console.WriteLine("It's white.");
}
else {
  Console.WriteLine("It's other color.");
}

Conditional Operator

int saleAmount = 1001;
int discount = saleAmount > 1000 ? 100 : 50;
Console.WriteLine($"Discount: {discount}");

Scope

bool flag = true;
if (flag)
{
  int value = 10;
  Console.WriteLine($"Inside the code block: {value}"); // Prints value.
}
Console.WriteLine($"Outside the code block: {value}"); // Gives error because value is declared inside the if code block.
Switch
string fruit = "apple";

switch (fruit)
{
  case "apple":
    Console.WriteLine($"App will display information for apple.");
    break;
  case "banana":
    Console.WriteLine($"App will display information for banana.");
    break;
  case "cherry":
    Console.WriteLine($"App will display information for cherry.");
    break;
  default:
    Console.WriteLine($"App will not display information about any fruit.");
    break;
}
Loop

Foreach

string[] names = { "Rowena", "Robin", "Bao" };

foreach (string name in names)
{
  Console.WriteLine(name); // "Rowena", "Robin", "Bao"
}

For

for (int i = 0; i < 10; i++)
{
  if (i > 5) {
    break;
  }
  Console.WriteLine(i); // 1, 2, 3, 4, 5
}

Do-While

Random random = new Random();
int current = 0;

do
{
  current = random.Next(1, 11);
  
  if (current >= 8) {
    continue;
  }
  
  Console.WriteLine(current);
} while (current != 7);

While

Random random = new Random();
int current = random.Next(1, 11);

while (current >= 3)
{
  Console.WriteLine(current);
  current = random.Next(1, 11);
}
Console.WriteLine($"Last number: {current}");
Casting

Casting truncates the value.

decimal myDecimal = 3.14m;
Console.WriteLine($"decimal: {myDecimal}"); // decimal: 3.14

int myInt = (int)myDecimal;
Console.WriteLine($"int: {myInt}"); // int: 3
To String
int first = 5;
int second = 7;
string message = first.ToString() + second.ToString();
Console.WriteLine(message); // 57
Parse
string first = "5";
string second = "7";
int sum = int.Parse(first) + int.Parse(second);
Console.WriteLine(sum); // 12
Parse
string value = "102";
int result = 0;
if (int.TryParse(value, out result))
{
   Console.WriteLine($"Measurement: {result}");
}
else
{
   Console.WriteLine("Unable to report the measurement.");
}
Convert

Convert rounds the value.

string value1 = "5";
string value2 = "7";
int result = Convert.ToInt32(value1) * Convert.ToInt32(value2);
Console.WriteLine(result); // 35
Sort
string[] pallets = [ "B14", "A11", "B12", "A13" ];

Console.WriteLine("Sorted...");
Array.Sort(pallets);
foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // A11, A13, B12, B14
}
Reverse
string[] pallets = [ "A11", "A13", "B12", "B14" ];

Console.WriteLine("Reversed...");
Array.Reverse(pallets);
foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // B14, B12, A13, A11
}
Clear
string[] pallets = [ "B14", "A11", "B12", "A13" ];
Console.WriteLine("");

Array.Clear(pallets, 0, 2);
Console.WriteLine($"Clearing 2 ... count: {pallets.Length}");
foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // null, null, B12, A13
}
Resize
string[] pallets =  ["B14", "A11", "B12", "A13" ];
Console.WriteLine("");

Array.Resize(ref pallets, 6);
Console.WriteLine($"Resizing 6 ... count: {pallets.Length}");

pallets[4] = "C01";
pallets[5] = "C02";

foreach (var pallet in pallets)
{
   Console.WriteLine($"-- {pallet}"); // B14, A11, B12, A13, C01, C02
}
Join
char[] valueArray = ['a', 'b', 'c']
Array.Reverse(valueArray);
// string result = new string(valueArray);
string result = String.Join("|", valueArray); // a|b|c
Console.WriteLine(result);
Split
string result = "123|456|789";
string[] items = result.Split('|');
foreach (string item in items)
{
   Console.WriteLine(item); // 123, 456, 789
}
Composite Formatting
string first = "Hello";
string second = "World";
string result = string.Format("{0} {1}!", first, second);
Console.WriteLine(result); // Hello World!
Formatting Currency
decimal price = 123.45m;
int discount = 50;
Console.WriteLine($"Price: {price:C} (Save {discount:C})"); // Price: $123.45 (Save $50.00)
Formatting Numbers
decimal measurement = 123456.78912m;
Console.WriteLine($"Measurement: {measurement:N} units"); // Measurement: 123,456.79 units
Console.WriteLine($"Measurement: {measurement:N4} units"); // Measurement: 123,456.7891 units
Formatting Percentage
decimal tax = .36785m;
Console.WriteLine($"Tax rate: {tax:P2}"); // Tax rate: 36.79%
Padding
string input = "Pad this";
Console.WriteLine(input.PadLeft(12)); // " 	Pad this"
Console.WriteLine(input.PadLeft(12, '*')); // "****Pad this"
Console.WriteLine(input.PadRight(12, '*')); // "Pad this****"
IndexOf
string message = "Find what is (inside the parentheses)";

int openingPosition = message.IndexOf('(');
int closingPosition = message.IndexOf(')');

Console.WriteLine(openingPosition); // 13
Console.WriteLine(closingPosition); // 36
Substring
string message = "What is the value <span>between the tags</span>?";

const string openSpan = "<span>";
const string closeSpan = "</span>";

int openingPosition = message.IndexOf(openSpan);
int closingPosition = message.IndexOf(closeSpan);

openingPosition += openSpan.Length;
int length = closingPosition - openingPosition;
Console.WriteLine(message.Substring(openingPosition, length)); // between the tags
LastIndexOf
string message = "hello there!";

int first_h = message.IndexOf('h');
int last_h = message.LastIndexOf('h');
Console.WriteLine($"For the message: '{message}', the first 'h' is at position {first_h} and the last 'h' is at position {last_h}."); 
// For the message: 'hello there!', the first 'h' is at position 0 and the last 'h' is at position 7.
IndexOfAny
string message = "Hello, world!";
char[] charsToFind = { 'a', 'e', 'i' };

int index = message.IndexOfAny(charsToFind);

Console.WriteLine($"Found '{message[index]}' in '{message}' at index: {index}."); // Found 'e' in 'Hello, world!' at index: 1.
Remove
string data = "12345John Smith          5000  3  ";
string updatedData = data.Remove(5, 20);
Console.WriteLine(updatedData); // 123455000  3  
Replace
string message = "This--is--ex-amp-le--da-ta";
message = message.Replace("--", " ");
message = message.Replace("-", "");
Console.WriteLine(message); // This is example data
Trim
string greeting = "      Hello World!       ";
Console.WriteLine($"[{greeting}]"); // "      Hello World!       "

string trimmedGreeting = greeting.TrimStart();
Console.WriteLine($"[{trimmedGreeting}]"); // "Hello World!       "

trimmedGreeting = greeting.TrimEnd();
Console.WriteLine($"[{trimmedGreeting}]"); // "      Hello World!"

trimmedGreeting = greeting.Trim();
Console.WriteLine($"[{trimmedGreeting}]"); // "Hello World!"
Contains
string songLyrics = "You say goodbye, and I say hello";
Console.WriteLine(songLyrics.Contains("goodbye")); // True
Console.WriteLine(songLyrics.Contains("greetings")); // False
Declaring Classes
// A type that is defined as a class is a reference type.

//[access modifier] - [class] - [identifier]
public class Customer
{
  // Fields, properties, methods and events go here...
}
Creating Objects
Customer object1 = new Customer(); // object1 is a reference to an allocated space that will know where the object exists.
Customer object2; // Reference to null

Customer object3 = new Customer(); 
Customer object4 = object3; // object4 has the same reference as object3. If any of both instances changes, the other one does as well. Not recommended to do that.
Constructors and initialization

Accept default values

Every .NET type has a default value. Typically, that value is 0 for number types, and null for all reference types. You can rely on that default value when it’s reasonable in your app.

Field initializers

public class Container
{
  private int _capacity;

  public Container(int capacity) => _capacity = capacity;
}

Primary Constructor

public class Container(int capacity)
{
  private int _capacity = capacity;
}

Object initializer

public class Person
{
  public required string LastName { get; set; }
  public required string FirstName { get; set; }
}

var p1 = new Person(); // Error! Required properties not set
var p2 = new Person() { FirstName = "Grace", LastName = "Hopper" };
Example 1
abstract class Shape
{
  public abstract int GetArea();
}

class Square : Shape
{
  private int _side;

  public Square(int n) => _side = n;

  // GetArea method is required to avoid a compile-time error.
  public override int GetArea() => _side * _side;

  static void Main()
  {
    var sq = new Square(12);
    Console.WriteLine($"Area of the square = {sq.GetArea()}");
  }
}
// Output: Area of the square = 144
Example 2
// Abstract class
abstract class BaseClass
{
  protected int _x = 100;
  protected int _y = 150;

  // Abstract method
  public abstract void AbstractMethod();

  // Abstract properties
  public abstract int X { get; }
  public abstract int Y { get; }
}

class DerivedClass : BaseClass
{
  public override void AbstractMethod()
  {
    _x++;
    _y++;
  }

  public override int X   // overriding property
  {
    get
    {
      return _x + 10;
    }
  }

  public override int Y   // overriding property
  {
    get
    {
      return _y + 10;
    }
  }

  static void Main()
  {
    var o = new DerivedClass();
    o.AbstractMethod();
    Console.WriteLine($"x = {o.X}, y = {o.Y}");
  }
}
// Output: x = 111, y = 161
Example 3
public abstract class Shape
{
  public string Color { get; set; }

  // Constructor of the abstract class
  protected Shape(string color)
  {
    Color = color;
    Console.WriteLine($"Created a shape with color {color}.");
  }

  // Abstract method that must be implemented by derived classes
  public abstract double CalculateArea();
}

public class Square : Shape
{
  public double Side { get; set; }

  // Constructor of the derived class calling the base class constructor
  public Square(string color, double side) : base(color)
  {
    Side = side;
  }

  public override double CalculateArea()
  {
    return Side * Side;
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    Square square = new Square("red", 5);
    Console.WriteLine($"Area of the square: {square.CalculateArea()}");            
  }
}