C# Fundamentals

Sun Feb 18 2024

Introduction to C#

C# is a modern, object-oriented, component-oriented and type-safe programming language developed by Microsoft. It has its roots in the C family of languages, making it loosely similar to C++, Java or JavaScript. C# programs run on .NET, a virtual execution system called the common language runtime (CLR) and a set of class libraries.

Two techniques for displaying literal-string data to the console.

Console.WriteLine("Hello ");     // WriteLine appends a new line after printing the output to the console
Console.WriteLine("World!");
    // Hello
    // World!

Console.Write("Hello World!");
Console.Write(" ");
Console.Write("This will print on the same line.");
    // Hello World! This will print on the same line.

In these simple blocks of code:

  • Console is a class
  • Write and WriteLine are methods. Methods are always followed by "()"- fancy term: method invocation operator. Each method has only one job.
  • Hello World! is a literal string
  • → The period is the member access operator used to navigate from a class to its methods
  • → the semicolon is the end of statement operator and it indicates the end of a command


Command-Line Interface


The structure of a CLI command consists of three parts:

  1. the driver: dotnet
  2. the command: new (new application) console (type of application)
  3. the command arguments: -o ./new_project_folder_name

This command will use a .NET program template to create a new console application in the specified folder location:

dotnet new console -o ./new_project_folder_name

To compile a build of your application, use the following command inside of your project directory:

dotnet build

This command will build the project and its dependencies into a set of binaries.

To run/execute your application, enter the following command:

dotnet run

Data types

Data types can be predefined - built into to the language or user-defined - users can create them.

Literal String

Console.WriteLine("Hello :)");
// Hello! :)

Members of string Type:

int howLong = myString.Length;
string upper = myString.ToUpper();
string lower = myString.ToLower();
bool hasHello = myString.Contains("Hello");
string replace = myString.Replace("a", "b");
string substring = myString.Substring(1, 3);       // first number: where to start from, second number: how many characters
bool firstName = myString.Equals("Lena"); 

Char Literal

A char literal is a single alphanumeric character surrounded by single quotes that we can use for presentation - not calculation.

Console.WriteLine('A');
// A

Members of char Type:

char myChar = 'a';

bool isCharWhiteSpace = char.IsWhiteSpace(myChar);         // checking if myChar is an empty space
bool isCharDigit = char.IsDigit(myChar);                   // checking if myChar is a digit
bool isCharPunctuation = char.IsPunctuation(myChar);        // checking if myChar is a punctuation mark

Integer literal

To display a whole number value in the console we use int literals.

Console.WriteLine(123)
// 123

Members of int Type:

int MaxValue = int.MaxValue;         // 2147483647
int MinValue = int.MinValue;         // -2147483648

Floating-point Literal

C# supports three data types to represent decimal numbers: float, double, and decimal. Each type supports varying degrees of precision.

To create a float, we append the letter F/f (literal suffix) after the number. The literal suffix tells the compiler you wish to work with a value of float type.

Console.WriteLine(0.25F)
// 0.25

Double literal

To create a double literal, we enter a decimal number. The compiler defaults to a double literal when a decimal number is entered without a literal suffix.

Console.WriteLine(2.625)
// 2.625

Decimal literal

To create a decimal literal, append the literal suffix m/M after the number. This will tell the compiler that we will be working with decimal values.

Console.WriteLine(12.39816m)
// 12.39816

Boolean literals

We use a bool literal to print a value representing true or false.

Console.WriteLine(true)
// True

Declaring variables


Variables are temporary storage containers for data. They can only hold values matching its specified data types.

To create a new variable we need to declare its data type followed by its name. Then we can assign a value to that variable using the equals sign. The assignment happens from right to left.

string myName;
myName = "Lena";

int myAge = 99;

Implicitly typed local variables

The var keyword tells the C# compiler that the data type is implied by the assigned value. Once the type is implied, the variable cannot be reassigned to a different data type.

Variables using the var keyword must be initialized.

var myAge = 99;

Byte(sbyte) and Short(ushort)

The byte type is used to store small 8-bit in features, of a value between 0 and 255.
The short type is used to store 16-bit integers.

Boolean literals


Working with string literals


Character escape sequences

In C#, the escape character sequence begins with a backslash "\" followed by the character you're escaping, for example \n for a new line, \t for tab, or \" to print the quotation mark.

Console.WriteLine("Hello\nWorld!");
// Hello
   World!

To print a single backslash, use \\.

Console.WriteLine("c:\\source\\repos");
// c:\source\repos

Verbatim string literal

To keep all whitespace and characters without the need to escape the backslash, we can use @.

Console.WriteLine(@"c:\source\repos
         - we're keeping the white space");
// c:\source\repos
    - we're keeping the white space

Unicode escape characters

We can use the \u escape sequence, then a four-character code representing some character in Unicode (UTF-16).

Console.WriteLine("\u3053\u3093\u306B\u3061\u306F World!");

// こんにちは World! (Kon'nichiwa World!)

Comparing strings

We can compare strings using Equals() method, but it's a case-sensitive method.
Here's a trick to compare strings regardless of letter-casing.

bool sameStrings = firstString.ToUpper() == anotherString.ToUpper();

String concatenation

To concatenate two strings together, you use the string concatenation operator, which is the plus symbol +.

string myName = "Lena";
string sayHello = "Hello";

Console.WriteLine(sayHello + " " + myName);

// Hello Lena

OR:

string string3 = String.Format("This is a greeting for {1}: {0}, {1}!", sayHello, myName);
Console.WriteLine(string3);

// This is a greeting for Lena: Hello, Lena!

OR:

string myName = "Lena";
string sayHello = "Hello ";       // notice the space added after Hello

Console.WriteLine(String.Concat(sayHello + " " + myName));

// Hello Lena

String interpolation

String interpolation combines multiple values into a single literal string using $ and { }. This is how the string literal becomes a template.

string myName = "Lena";
string sayHello = "Hello";
string greeting = $"{sayHello} {myName}";

Console.WriteLine(greeting);

// Hello Lena

StringBuilder Type

StringBuilder doesn't create a new copy of a string every time, but changes the original one. It has quite a few methods to work with strings.

StringBuilder string1 = new StringBuilder();

string1.Append("To-Do List: ");             // on the same line
string1.AppendLine("Read a chapter.");      // adds a new line after
string1.AppendLine("Do shopping.");         

Console.WriteLine(string1.ToString());

Converting between data types

There are three ways if converting data types: implicit conversion, explitic conversion, helpers.

Implicit conversion:

int a = 123541;
long l = a;

Explicit conversion:

double d = 123456789.0;
int a = (int) d;

// because double contains more information than int, 
and some part of the data will get lost in the conversion,
we need to explicitly confirm the output data type

Working with numeric values


Add two numeric values

int firstNumber = 3;
int secondNumber = 14;
Console.WriteLine(firstNumber + secondNumber);

// 17

If we use the + symbol with both string and integer values, it will carry out concatenation.

We can use parentheses to clarify our intention of adding to the compiler, as any operations that happen inside of parentheses are always resolved first. Since both our variables are integers, the plus symbol will add both numbers together - NOT concatenate them.

int firstNumber = 3;
int secondNumber = 14;
Console.WriteLine(firstNumber + " is smaller than " + secondNumber + ". ");
Console.WriteLine(firstNumber + secondNumber);

// 3 is smaller than 14. 
   17

Math operations

We use: + for addition, - for subtraction, * for multiplication, / for division.

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


Console.WriteLine("Sum: " + sum);
Console.WriteLine("Difference: " + difference);
Console.WriteLine("Product: " + product);
Console.WriteLine("Quotient: " + quotient);

// Sum: 12
   Difference: 2
   Product: 35
   Quotient: 1 (int cannot contain values after the decimal)

Perform division using literal decimal data

To get the correct result, we need to use a data type that supports fractional digits after the decimal point like decimal. For this to work, the quotient and at least one of numbers being divided must be of type decimal.

decimal decimalQuotient = 7 / 5.0m;
    OR
decimal decimalQuotient = 7.0m / 5.0m;
    
NOT decimal decimalQuotient = 7 / 5;


Console.WriteLine(decimalQuotient);

    // 1.4

Modulus

The modulus operator % gives us the remainder of int division. We can use it with $ and { }.

Console.WriteLine($"Modulus of 200 / 5 is {200 % 5}");
Console.WriteLine($"Modulus of 7 / 5 is {7 % 5}");

// Modulus of 200 / 5 is 0
   Modulus of 7 / 5 is 2

Compound assignment operators

This addition assignment operator += adds and assigns the value on the right of the operator to the value on the left of the operator

int value = 0;     // value is now 0.
value = value + 5; // value is now 5.
value += 5;        // value is now 10.
value -= 4;        // value is now 6.

The ++ operator increments the value of the variable by 1.

int value = 3;     // value is now 0.
value = value + 1; // value is now 4.
value++;           // value is now 5.
value--;           // value is now 4.

If we use the operator ++ before the value (++value), the increment will happen before the value is retrieved. Likewise, value++ will increment the value after the value has been retrieved.


Working with Date and Time

C# has two built-in data types to work with time and dates: DateTime and TimeSpan.

This is a few examples of how to use them:


DateTime myBirthDayIn2024 = new DateTime(2024, 06, 01);      //01 / 06 / 2024 00:00:00
DateTime myBirthDayIn2025 = myBirthDayIn2024.AddYears(1);    //01 / 06 / 2025 00:00:00

DateTime startTime = DateTime.Now;                            //10/02/2024 21:56:25
TimeSpan workTime = new TimeSpan(8, 0, 0);
DateTime endTime = startTime.Add(workTime);                    //11/02/2024 05:56:25

Console.WriteLine(startTime.ToLongDateString());             //10 February 2024

Console.WriteLine(endTime.ToShortTimeString());             //05:56

If, else, else if

The curly braces can be omitted if there is only a single statement following if.

Console example:

Console.WriteLine("Enter a first number:");
string stringValue1 = Console.ReadLine();
Console.WriteLine("Enter a second  number:");
string stringValue2 = Console.ReadLine();
int intValue1 = int.Parse(stringValue1);
int intValue2 = int.Parse(stringValue2);

if (intValue1 == intValue2)
{
  Console.WriteLine("The values are equal!");
}
else if (intValue1 < intValue2)
{
    Console.WriteLine("The first value is smaller!");
}
else
{
  Console.WriteLine("The second value is smaller!");
}

Switch

The switch statement will not work with float and double.
Care labels use a pattern: constant or relational.
Each case must be unique.
The first true statement will get executed.


Console.WriteLine("Enter your age:");
int age = int.Parse(Console.ReadLine());

switch (age)
{
    case < 13:
        Console.WriteLine("You're a child.");
        break;
    case > 12:                                          // two cases can share the same code
    case < 18:
        Console.WriteLine("You're a teenager.");
        break;
    case 25:
        Console.WriteLine("You're exactly 25 years old.");
        break;
    case > 65:
        Console.WriteLine("I'd never call you old, but we're all know that you're EXTREMELY mature. :)");
        break;
    case:
        Console.WriteLine("You're an adult.");
        break;
}

Loop options


While statement

Console.WriteLine("Enter a number: ");
int startNumber = int.Parse(Console.ReadLine());
int maxNumber = 5;

while (startNumber <= maxNumber)
{
    Console.WriteLine(startNumber);
    startNumber++;
}
// (if we enter 1)
    1
    2
    3
    4
    5

For loop

For loops use keywords continue and break. If put after a statement, the loop will either execute a statement at a particular point, or stop the loop once that point is met.

Structure:

for (initialization; Boolean; iterator)
{
    statements
}

Example:

int sum = 0;

for (int i = 0; i < 10; i++)
{
    sum = sum + i;
}

Console.WriteLine(sum);

Passing variables by reference

There are two kinds of types in C#: reference types (classes, interfaces and delegates) and value types (enumerations and structs). A variable of a reference type contains a reference to its data. A variable of a value type contains its data directly. Operations on one variable can affect the object referenced by the other variable. (source: https://learn.microsoft.com/)

The ref keyword requires the variable to be initialized before entering the method. Without ref any changes to the variable made inside of the method will not change the variable itself.
In simple words: if arguments passed to a method are preceded by ref, they can be changed by this method.

The out keyword requires the variable to be initialized inside of the method. Apart from this detail, the out keyword behaves just like ref.


static void Main(string[] args)
{
    int add;
    int mult;
    AddAndMultiply(3, 4, out add, out multiply);

    Console.WriteLine(add);           //7
    Console.WriteLine(multiply);      //12
}


public static void AddAndMultiply(int a, int b, out int added, out int multiplied)
{
    added = a + b;
    multiplied = a * b;
}
    

Methods


Structure:

access modifier return type MethodName (Parameters)
{
    statements
}

A few things to know about methods in C#:

If the return type of a method is specified, the method MUST return a value of that type. A method that doesn't return anything is of type void.

Method overload happens when we use the same function multiple times. This is possible if the functions take different parameters or return different data types.

The order of a method's parameters is not always important, as we name our parameters when calling a method.

Method(b: 3, a: 1);

We can give a method default parameters, which do not to be listed when calling this method:

public static int AddNumbers (int a, int b, int c = 4);

Explore .NET Class Library

.NET Class Library is a prewritten collection of coding resources (classes with methods) that we can use to build our applications.

The format of calling methods of a class in the .NET Class Library is: ClassName.MethodName(),


Stateful vs stateless methods

The term state is used to describe the condition of the execution environment at a specific moment in time.

Stateless/static methods work without changing any values stored in memory. The example would be Console.WriteLine() as it doesn't impact the state of the application in any way or relies on any stored values.

When calling a stateless method, there is no need to create a new instance of its class first.

Stateful/instance methods rely on values stored in memory by previous lines of code that have already been executed. They can modify the state of the application by updating values or storing new values in memory. They keep track of their state in variables defined on the class.

When calling a stateful method, we need to create an instance of the class, and access the method on the object.

A single class can support both stateful and stateless methods. However, when you need to call stateful methods, you must first create an instance of the class so that the method can access state.


Classes


Class structure:

 public class ClassName                  // access modifier, class definition
{                                        // class body 
    public int num1;                     // fields: class level variables 
    public string string1;

    public void Method()                 // methods 
    {
        Console.WriteLine("Hello!");
    }
}

Class may also contain events and properties.

Classes are a reference type.


Access modifiers

public: accessible from outside

private: only accessible from within the class

protected: only accessible from within the class and its inheriters

internal:


Example:

 internal class Employee
 {
     public string firstName;
     public string lastName;
     public string email;

     public int numberofHoursWorked;
     public double wage;
     public double hourlyRate;

     const int minHoursWorked = 1;

     public DateTime birthDay;

     public void PerformWork()
     {
         PerformWork(minHoursWorked);
         //numberofHoursWorked++;
         //Console.WriteLine($"{firstName} {lastName} has worked for {numberofHoursWorked}.");
     }

     public void PerformWork(int numberofHours)
     {
         numberofHoursWorked += numberofHours;
         Console.WriteLine($"{firstName} {lastName} has worked for {numberofHours}.");

     }

     public double ReceiveWage(bool resetHours = true)
     {
         wage = numberofHoursWorked * hourlyRate;
         Console.WriteLine($"{firstName} {lastName} has received £{wage} for {numberofHoursWorked} hours.");

         if (resetHours)
         {
             numberofHoursWorked = 0;
         }
         return wage;
     }

     public void DisplayEmployeeDetails()
     {
         Console.WriteLine($"First name: \t{firstName} \nLast name: \t{lastName} has received £{wage} for {numberofHoursWorked} hours.");
     }
 }

Constructor

When we instantiate an object a constructor method is called.
Constructors can be custom or deafult - a default one will be called if a constractor is not specified. However, once we create a custom constructos, the default one is no longer available.
A constructor doesn't have a return type and it shares its name with the name of the class.
W use a constructor to set the initial values for the fields.

public class Employee
{
    public string firstName;
    public int age;

    public Employee(string name, int ageInt)
    {
        firstName = name;
        age = ageInt;
    }
}

We can also use primary constructor which has been introduced with C# 12.

public class Employee(string name, int ageInt)
{
}

Just like mothods, constructors can also be overloaded, if we give each a different set of parameters.

public Employee(string first, string last, string em, DateTime bd) : this(first, last, em, bd, 0)
     { }                                //4 parameters

public Employee(string first, string last, string em, DateTime bd, double rate)
{                                       // 5 parameters
    firstName = first;
    lastName = last;
    email = em;
    hourlyRate = rate;
    birthDay = bd;
}

Creating an instance of a class

An instance of a class is called an object. We create a new instance of a class with the new operator.

Let's create an object of a class from the example above.
We will use the construvtor in order to do that.

Employee employee = new Employee("Lena", 55);

variable type | variable name = new (class ← it can be omitted) | arguments

Let's invoke a method on the created Employee.

employee.PerformWork();

Let's change a field.

employee.firstName = "Magdalena";

Let's return a value from a method.

int salary = employee.ReceiveWage();

Roll dice example:

Random dice = new Random();

OR (in the later versions of .NET Runtime)

Random dice = new();

Some methods require you to create an instance of a class before you call them.

This line would cause an error:

int roll = Random.Next();

To get rid of this error, we need to create an instance of the Random class (before accessing the Next() method).

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

// random int between 1 and 6

Enumerations


Enums are a special class that contains a set of named integer constants.

public enum Colors {
    Red,                  // 0 (if no other value has been assigned)
    Green,                // 1
    Blue,                 // 2
    Brown,                // 3
    Pink                  // 4
}

OR we can specify the integer values for our colors ourselves

public enum Colors {
    Red = 10,                  // 10
    Green = 12,                // 12
    Blue = 13,                 // 13
    Brown = 24,                // 24
    Pink = 35                  // 35
}

Let's access one of the colour names:

Console.WriteLine(Colors.Red + "is my favourite colour.");          
           // Red is my favourite colour.

Let's access the integer values.

Console.WriteLine(Colors.Pink + "'s integer value is " + (int)Colors.Pink);
           // Pink's integer value is 35

Struct


Struct is a value type and represents a custom data structure. We can create it with a new keyword. Can contain methods and other members.

struct WorkTask
{
    public string description;
    public int hours;

    public void PerformWorkTask()
    {
        Console.WriteLine($"Task: {description} has been performed. It took {hours} hours.")
    }
}
           
WorkTask task;

task.description = "Preparing documents";
task.hours = 2;
task.PerformWorkTask();         //Task: Preparing documents has been performed. It took 2 hours.

Lists of data


Arrays

  • * all variables must be of the same type,
  • * accessed through index
  • * are reference type, stored on the heap,
  • * arrays are created when we use the keyword new
  • * array size is set while initialization

Initiating an array:


string[] booksToRead;
string[] booksToRead = new string[5];      //we need to specify the size of array
DateTime[] readingTime;

Populating an array:

string[] booksToRead = new string[5] {"Title1", "Title 2", "Title 3", "Title 4", "Title 5"};

int[] numbers = new int[] {45, 99, 555};        //we can omit size value if we pass in an initializer
int[] numbers = new int[] {45, 99, 555};            OR      int[] numbers = [45, 99, 555];

Let's access the elements of an array:

numbers[0] = 5;

foreach loop

foreach (int num in numbers)
{
    var randomNum = new Random().Next(30);
    var together = num + randomNum;
           
     Console.WriteLine(num + ", " + randomNum + ", " + together);
}

                    //      5, 4, 9
                            99, 26, 125
                            555, 3, 558

The Array Base Class

  • CopyTo() and Length
    
    
    int[] numbers = [1, -8, 23, 11];
    int lentgh = numbers.Length;
    
    int[] numbersCopy = new int[lentgh];    //we need an array to copy our array into
    
    numbers.CopyTo(numbersCopy, 0);         //copy to numbersCopy, start with index 0 = copy everthing
    
    foreach (int num in numbersCopy)
    {
        var randomNum = new Random().Next(30);
        var together = num + randomNum;
        Console.WriteLine(num + ", " + randomNum + ", " + together);
    }       
            //  -8, 5, -3
                1, 14, 15
                11, 12, 23
                23, 17, 40
  • Sort() and Reverse()
    int[] numbers = [1, -8, 23, 11];
    
    Array.Sort(numbers);
    
    foreach (int num in numbers)
    {
        var randomNum = new Random().Next(30);
        var together = num + randomNum;
        Console.WriteLine(num + ", " + randomNum + ", " + together);
    }
           // Hello, World!
                -8, 1, -7
                1, 13, 14
                11, 10, 21
                23, 0, 23

    Collections

      • * i.e. List
      • * arrays have a lot of limitations, which collections do not
      • * we don't need to specify the size of a collection - it will change dynamically
      • * we use List<T> to create a collection, where T stands for type
      • * all elements of a List need to be of the same type

      //creating a List
      
      List<int> numbers = new List<int> { 1, 2, 3 };
      
      OR 
      
      List<int> numbers = new() { 1, 2, 3 };
      OR 
      
      List<int> numbers = new List<int>();
      
      //adding to a List numbers.Add(78);
      //removing from a List numbers.Remove(3);
      //accessing an element int selectedEl = numbers[2]; Console.WriteLine(selectedEl); //78
      //checking the size int length = numbers.Count; Console.WriteLine(length); //3
      //inserting an element into a specific position numbers.Insert(0, 3000); Console.WriteLine(numbers);
      //let's remove everything from the numbers collection numbers.Clear(); Console.WriteLine(numbers.Count); //0

      Sources:

      https://learn.microsoft.com/en-us/dotnet/csharp/tour-of-csharp

      https://learn.microsoft.com/en-gb/training/modules/csharp-call-methods/3-call-methods

      https://learn.microsoft.com/en-gb/training/

Illustration for the C# Fundamentals

Comments (0)


Be the first to leave a comment