CodeEazy

C# Logo

C# Programming Made Eazy

1. Introduction to C# and .NET (C# 12 / .NET 8)

What is C#?

C# is a modern, multi-paradigm programming language developed by Microsoft. It is widely used for building web apps, APIs, desktop software, mobile apps (via Xamarin or MAUI), cloud-based services, and games (using Unity). With each version, C# evolves to offer more concise, expressive, and performant code. C# 12 introduces features like primary constructors for non-record classes and default lambda parameters.

Overview of the .NET Ecosystem (.NET 8)

.NET 8 is the latest long-term support (LTS) release of the .NET platform. It’s a unified, cross-platform runtime and development framework that allows you to build apps for Windows, Linux, macOS, iOS, Android, and the web.

  • Cross-Platform: Run C# code on any OS.
  • Unified Platform: Web, desktop, mobile, cloud, and IoT development in one ecosystem.
  • Performance: Major runtime and JIT improvements make .NET 8 faster than previous versions.
  • Language Innovation: Latest C# features like raw string literals, primary constructors, and improved pattern matching.

Installing Visual Studio / VS Code + .NET 8 SDK

To begin C# development with .NET 8:

  • Visual Studio 2022+ (17.8 or later): Fully supports .NET 8 and C# 12.
  • Visual Studio Code: Lightweight and cross-platform with C# Dev Kit and .NET SDK.
  • .NET SDK 8: Required to build and run modern C# applications.

Download from dotnet.microsoft.com.

First Console App: Hello World (Top-Level Statements)

Starting with C# 9 and refined in C# 12, you can write your first app using top-level statements—no need for boilerplate class or Main method:

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

This is a valid and complete C# program in .NET 8. To create and run it:

dotnet new console -n HelloWorldApp
    cd HelloWorldApp
    dotnet run

C# File Structure and Compilation Process

Modern C# supports file-scoped namespaces, top-level statements, and global usings to reduce clutter:

namespace HelloWorld;
    
    Console.WriteLine("Hello from C# 12 and .NET 8!");

The compilation process:

  • C# code is compiled to Intermediate Language (IL).
  • IL is JIT-compiled and executed by the Common Language Runtime (CLR).
  • The .NET runtime handles memory management, garbage collection, and type safety.

2. Basic Syntax and Data Types (C# 12)

Variables and Constants

Variables are used to store data values. In C#, you must declare a variable with its type. Constants are immutable values declared using the const keyword.

// Variable declaration
    int age = 30;
    double price = 99.99;
    
    // Constant declaration
    const double Pi = 3.14159;

Data Types in C#

C# is a statically typed language, and variables must be declared with a type. Common built-in types include:

  • int: Whole numbers (e.g., 1, 100)
  • double: Decimal numbers (e.g., 3.14)
  • char: A single character (e.g., 'A')
  • string: A sequence of characters (e.g., "Hello")
  • bool: Boolean values (true or false)
int score = 100;
    double temperature = 36.6;
    char grade = 'A';
    string name = "Kumar";
    bool isActive = true;

Type Conversion and Parsing

C# supports implicit and explicit type conversions. You can also convert strings to numeric types using parsing methods.

// Implicit conversion
    int x = 10;
    double y = x; // OK
    
    // Explicit conversion
    double a = 9.8;
    int b = (int)a; // Requires cast
    
    // Parsing from string
    string input = "123";
    int number = int.Parse(input);

Comments and Naming Conventions

Comments help explain code. C# supports single-line and multi-line comments.

// This is a single-line comment
    
    /*
     This is a multi-line comment
     Spanning multiple lines
    */

Naming Conventions:

  • Use camelCase for local variables and parameters
  • Use PascalCase for method names, class names, and properties
  • Avoid using abbreviations or single-letter variable names

Operators in C#

C# provides various operators for performing operations:

  • Arithmetic: +, -, *, /, %
  • Comparison: ==, !=, >, <, >=, <=
  • Logical: && (AND), || (OR), ! (NOT)
// Arithmetic
    int sum = 5 + 3;
    int mod = 10 % 3;
    
    // Comparison
    bool isEqual = (5 == 5);
    
    // Logical
    bool result = (true && false) || true;

3. Control Flow (C# 12)

if, else if, else Statements

Conditional statements allow you to execute blocks of code based on conditions. C# uses if, else if, and else to control flow.

int marks = 85;
    
    if (marks >= 90)
        Console.WriteLine("Grade A");
    else if (marks >= 75)
        Console.WriteLine("Grade B");
    else
        Console.WriteLine("Grade C");

switch Statement

The switch statement is useful when comparing a variable against multiple values. In C# 8+, pattern matching in switch is supported. C# 12 supports even more powerful patterns.

int day = 3;
    
    switch (day)
    {
        case 1:
            Console.WriteLine("Monday");
            break;
        case 2:
            Console.WriteLine("Tuesday");
            break;
        case 3:
            Console.WriteLine("Wednesday");
            break;
        default:
            Console.WriteLine("Invalid day");
            break;
    }

Loops: for, while, do-while

Loops allow you to repeat code multiple times. Common loop types include:

  • for loop: When the number of iterations is known
  • while loop: Repeats while a condition is true
  • do-while loop: Executes at least once before checking the condition
// for loop
    for (int i = 1; i <= 5; i++)
    {
        Console.WriteLine(i);
    }
    
    // while loop
    int j = 1;
    while (j <= 5)
    {
        Console.WriteLine(j);
        j++;
    }
    
    // do-while loop
    int k = 1;
    do
    {
        Console.WriteLine(k);
        k++;
    } while (k <= 5);

break and continue

The break statement exits the loop early, while continue skips the current iteration and moves to the next.

for (int i = 1; i <= 5; i++)
    {
        if (i == 3)
            continue; // skip when i is 3
    
        if (i == 5)
            break; // exit loop when i is 5
    
        Console.WriteLine(i);
    }

Nested Loops and Pattern Printing

Nested loops are loops inside other loops. These are useful for tasks like printing patterns or processing multidimensional arrays.

// Pattern: right-angle triangle
    int rows = 5;
    for (int i = 1; i <= rows; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            Console.Write("* ");
        }
        Console.WriteLine();
    }

4. Methods & Parameters (C# 12)

Defining and Calling Methods

A method is a block of code that performs a specific task. It improves code reusability and readability. You define a method using a return type, a name, and parameters (if any).

// Method definition
    void Greet()
    {
        Console.WriteLine("Hello, welcome to C#!");
    }
    
    // Calling the method
    Greet();

Parameters and Return Types

Methods can accept input parameters and return values using the return keyword.

// Method with parameters and return value
    int Add(int a, int b)
    {
        return a + b;
    }
    
    // Usage
    int result = Add(5, 3);
    Console.WriteLine($"Sum = {result}");

Method Overloading

Method overloading allows multiple methods with the same name but different parameters. The compiler chooses the appropriate method based on the arguments.

void Show()
    {
        Console.WriteLine("No parameters");
    }
    
    void Show(string name)
    {
        Console.WriteLine($"Hello, {name}");
    }
    
    Show();         // No parameters
    Show("Kumar");  // Hello, Kumar

ref, out, and params Keywords

ref: Passes a variable by reference. The original value is modified.

void Increment(ref int num)
    {
        num++;
    }
    
    int x = 5;
    Increment(ref x);
    Console.WriteLine(x); // 6

out: Passes a variable by reference, but it doesn’t need to be initialized before calling.

void GetValues(out int a, out int b)
    {
        a = 10;
        b = 20;
    }
    
    int val1, val2;
    GetValues(out val1, out val2);

params: Allows passing a variable number of arguments as an array.

int Sum(params int[] numbers)
    {
        int total = 0;
        foreach (var num in numbers)
            total += num;
        return total;
    }
    
    int totalSum = Sum(1, 2, 3, 4); // 10

Recursion

Recursion is when a method calls itself to solve a smaller instance of a problem. A common example is calculating factorial:

int Factorial(int n)
    {
        if (n == 0)
            return 1;
        return n * Factorial(n - 1);
    }
    
    int fact = Factorial(5); // 120

5. Object-Oriented Programming (C# 12)

Classes and Objects

A class is a blueprint for creating objects. An object is an instance of a class containing real values.

// Class definition
    class Person
    {
        public string Name;
        public void SayHello()
        {
            Console.WriteLine($"Hello, I am {Name}");
        }
    }
    
    // Creating and using an object
    Person p = new Person();
    p.Name = "Kumar";
    p.SayHello();

Fields, Properties, and Methods

- Fields are variables declared inside a class.
- Properties provide controlled access to fields.
- Methods define behavior.

class Product
    {
        private int _price;
    
        public int Price
        {
            get { return _price; }
            set { _price = value; }
        }
    
        public void Display()
        {
            Console.WriteLine($"Price: {Price}");
        }
    }

Constructors and Destructors

A constructor is a special method invoked when an object is created. A destructor (~ClassName) is called by the garbage collector to free resources (rarely used in .NET).

class Car
    {
        public string Model;
    
        // Constructor
        public Car(string model)
        {
            Model = model;
        }
    
        // Destructor
        ~Car()
        {
            Console.WriteLine("Destructor called");
        }
    }

Access Modifiers (public, private, protected)

  • public: Accessible from anywhere.
  • private: Accessible only within the class.
  • protected: Accessible within the class and derived classes.
class Example
    {
        private int secret = 42;
        public int visible = 100;
    }

Inheritance and the base Keyword

Inheritance allows a class to acquire the members of another class. The base keyword is used to call the base class constructor or method.

class Animal
    {
        public void Speak() => Console.WriteLine("Animal speaks");
    }
    
    class Dog : Animal
    {
        public void Bark() => Console.WriteLine("Dog barks");
    }
    
    Dog d = new Dog();
    d.Speak();
    d.Bark();

Method Overriding and Polymorphism

Method overriding lets a subclass provide a specific implementation of a method declared in the base class. The virtual and override keywords enable this.

class Animal
    {
        public virtual void Speak() => Console.WriteLine("Animal speaks");
    }
    
    class Cat : Animal
    {
        public override void Speak() => Console.WriteLine("Cat meows");
    }
    
    Animal a = new Cat();
    a.Speak(); // Cat meows (runtime polymorphism)

Interfaces and Abstract Classes

- An interface defines a contract with only method/property signatures.
- An abstract class can have both abstract and concrete methods.

// Interface
    interface ILogger
    {
        void Log(string message);
    }
    
    // Abstract class
    abstract class Shape
    {
        public abstract double Area();
        public void Describe() => Console.WriteLine("Shape description");
    }
    
    // Implementation
    class Rectangle : Shape
    {
        public int Width = 5;
        public int Height = 10;
    
        public override double Area() => Width * Height;
    }

6. Collections and Arrays (C# 12)

Arrays in C#

Arrays are fixed-size collections that store elements of the same type. C# supports 1D, 2D (multi-dimensional), and jagged (array of arrays) arrays.

1D Array

int[] numbers = { 1, 2, 3, 4, 5 };
    Console.WriteLine(numbers[2]); // Output: 3

2D Array

int[,] matrix = {
        { 1, 2 },
        { 3, 4 }
    };
    Console.WriteLine(matrix[1, 1]); // Output: 4

Jagged Array

int[][] jagged = new int[2][];
    jagged[0] = new int[] { 1, 2 };
    jagged[1] = new int[] { 3, 4, 5 };
    Console.WriteLine(jagged[1][2]); // Output: 5

Common Collections

The System.Collections.Generic namespace provides flexible and type-safe collection classes.

List<T>

A dynamic array that grows/shrinks as needed.

List<string> fruits = new() { "Apple", "Banana" };
    fruits.Add("Cherry");
    Console.WriteLine(fruits[1]); // Banana

Dictionary<TKey, TValue>

Stores key-value pairs for fast lookups.

Dictionary<string, int> ages = new()
    {
        ["Alice"] = 25,
        ["Bob"] = 30
    };
    Console.WriteLine(ages["Bob"]); // 30

HashSet<T>

Stores unique values only, no duplicates.

HashSet<int> uniqueNumbers = new() { 1, 2, 2, 3 };
    Console.WriteLine(uniqueNumbers.Count); // 3

foreach Loop

The foreach loop iterates over arrays or collections without needing index tracking.

foreach (string fruit in fruits)
    {
        Console.WriteLine(fruit);
    }

Sorting and Searching Collections

Sorting a List

List<int> numbers = new() { 5, 2, 8, 1 };
    numbers.Sort();
    Console.WriteLine(string.Join(", ", numbers)); // 1, 2, 5, 8

Searching in a List

bool hasEight = numbers.Contains(8);  // true
    int index = numbers.IndexOf(5);       // 2

Sorting Dictionary by Key

var sorted = ages.OrderBy(kv => kv.Key);
    foreach (var kv in sorted)
        Console.WriteLine($"{kv.Key}: {kv.Value}");

7. Exception Handling (C# 12)

try, catch, finally

In C#, exceptions are handled using try, catch, and finally blocks. - try contains the code that might throw an exception. - catch handles the exception. - finally executes regardless of whether an exception occurs.

try
{
    int x = 10;
    int y = 0;
    int result = x / y;
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}
finally
{
    Console.WriteLine("Cleanup or final steps.");
}

Common Exception Types

  • System.Exception: Base class for all exceptions
  • DivideByZeroException: Thrown when dividing by zero
  • NullReferenceException: Object reference not set
  • IndexOutOfRangeException: Accessing an invalid index
  • InvalidOperationException: Operation not valid for the object state
  • FileNotFoundException: File not found during file operations

Throwing Exceptions

You can throw exceptions using the throw keyword:

int age = -1;
if (age < 0)
    throw new ArgumentException("Age cannot be negative.");

Creating Custom Exceptions

You can define your own exception types by inheriting from Exception:

class CustomException : Exception
{
    public CustomException(string message) : base(message) { }
}

// Usage
throw new CustomException("Something went wrong in your logic.");

8. File I/O Basics (C# 12)

Reading and Writing Text Files

The System.IO namespace provides classes for file and directory operations. Common tasks include reading from and writing to text files.

Write Text to a File

string filePath = "example.txt";
File.WriteAllText(filePath, "Hello, this is a sample text.");

Read Text from a File

string content = File.ReadAllText("example.txt");
Console.WriteLine(content);

Read All Lines

string[] lines = File.ReadAllLines("example.txt");
foreach (var line in lines)
    Console.WriteLine(line);

Checking File Existence

Before performing operations, it’s good to check if the file exists:

if (File.Exists("example.txt"))
{
    Console.WriteLine("File exists.");
}

Working with Directories

The Directory class allows creating, deleting, and checking folders:

// Create a new directory
string folderPath = "MyFolder";
Directory.CreateDirectory(folderPath);

// List files in a directory
string[] files = Directory.GetFiles(folderPath);
foreach (var file in files)
    Console.WriteLine(file);

// Check if a directory exists
if (Directory.Exists(folderPath))
{
    Console.WriteLine("Directory found.");
}

9. Introduction to LINQ (C# 12)

What is LINQ?

LINQ (Language Integrated Query) is a powerful feature in C# that allows you to write queries directly within your C# code to retrieve and manipulate data from collections, databases, XML, and more. It brings SQL-like query capabilities into the C# language itself.

Basic LINQ Queries

int[] numbers = { 1, 2, 3, 4, 5, 6 };

// LINQ query syntax
var evenNumbers = from num in numbers
                  where num % 2 == 0
                  select num;

foreach (var n in evenNumbers)
    Console.WriteLine(n);

Filtering, Projection, Aggregation

Filtering

var highScores = numbers.Where(n => n > 3);
foreach (var score in highScores)
    Console.WriteLine(score);

Projection (Select)

var squares = numbers.Select(n => n * n);
foreach (var sq in squares)
    Console.WriteLine(sq);

Aggregation

int sum = numbers.Sum();
int count = numbers.Count();
int max = numbers.Max();
int min = numbers.Min();

LINQ with Lists and Arrays

LINQ works with any collection that implements IEnumerable<T>, including arrays and generic lists.

List<string> names = new() { "Alice", "Bob", "Charlie" };

var shortNames = names.Where(name => name.Length <= 4);
foreach (var name in shortNames)
    Console.WriteLine(name);

LINQ makes data processing more readable and expressive, reducing the need for complex loops and conditionals.