C# and .NET Framework Core Concepts Q&A

Garbage Collection Importance and .NET Architecture

Importance of Garbage Collection (GC) in .NET

Garbage Collection (GC) is an automatic memory management feature in the .NET Framework that plays a crucial role in improving application performance and memory efficiency. Its importance includes:

  • Automatic Memory Management: Developers do not need to manually allocate or deallocate memory, reducing complexity and potential errors.
  • Improved Application Performance: By efficiently reclaiming unused memory, GC ensures resources are available for active processes.
  • Object Lifetime Tracking: GC automatically tracks which objects are still reachable and which are eligible for collection.
  • Prevents Memory Leaks: It automatically cleans up objects that are no longer referenced, preventing common memory leak issues.
  • Efficient Memory Usage: It compacts the heap, reducing fragmentation and making better use of available memory space.

.NET Framework Architecture

The .NET Framework is a platform for building and running Windows applications. It consists primarily of the Common Language Runtime (CLR) and a rich set of Class Libraries.

1. .NET Applications

These are user-developed applications (like Web, Desktop, Console applications) that run on top of the .NET Framework.

2. Base Class Library (BCL)

The BCL provides a large set of pre-built reusable classes. This includes libraries for file I/O, networking, collections, databases, and more.

3. Common Language Runtime (CLR)

The CLR is the execution engine of the .NET Framework. Key components include:

  • Garbage Collector (GC): Manages memory and object lifetime.
  • JIT Compiler: Converts MSIL (Microsoft Intermediate Language) to native machine code at runtime.

Jagged Arrays and C# Multidimensional Array Example

What is a Variable Size Array?

A Variable Size Array (also called a jagged array) in C# is an array whose elements are arrays of different sizes. This allows you to store data in a non-uniform (ragged) format.

C# Program: Multidimensional Array for Student Marks

The following program creates a standard multidimensional array (not a jagged array) to store the marks of three students in four subjects:

using System;

class Program
{
    static void Main()
    {
        int[,] marks = new int[3, 4];

        for (int student = 0; student < 3; student++)
        {
            Console.WriteLine($"Enter marks for Student {student + 1}:");
            for (int subject = 0; subject < 4; subject++)
            {
                Console.Write($"Subject {subject + 1}: ");
                marks[student, subject] = Convert.ToInt32(Console.ReadLine());
            }
        }

        Console.WriteLine("\nMarks of all students:");
        for (int student = 0; student < 3; student++)
        {
            Console.Write($"Student {student + 1}: ");
            for (int subject = 0; subject < 4; subject++)
            {
                Console.Write(marks[student, subject] + " ");
            }
            Console.WriteLine();
        }
    }
}

C# LINQ Query for Selecting Odd and Even Numbers

This C# program uses LINQ (Language Integrated Query) query syntax to separate odd and even numbers from a list ranging from 1 to 30.

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = Enumerable.Range(1, 30).ToList();

        // LINQ Query for Even Numbers
        var evenNumbers = from num in numbers
                          where num % 2 == 0
                          select num;

        // LINQ Query for Odd Numbers
        var oddNumbers = from num in numbers
                         where num % 2 != 0
                         select num;

        Console.WriteLine("Even Numbers (1 to 30):");
        foreach (var num in evenNumbers)
        {
            Console.Write(num + " ");
        }

        Console.WriteLine("\n\nOdd Numbers (1 to 30):");
        foreach (var num in oddNumbers)
        {
            Console.Write(num + " ");
        }
    }
}

Static Classes and Constructors in C#

Static Class Concept

A static class in C# is a class that:

  • Cannot be instantiated: You cannot create objects from it using the new keyword.
  • Can only contain static members (methods, fields, properties).
  • Is used when the class is only meant to provide helper methods or global utility functions (e.g., System.Math).

Static Constructor Concept

A static constructor:

  • Is used to initialize static data or perform actions that need to be done only once per application domain.
  • Is called automatically before the first instance is created or before any static members are referenced.
  • Has no parameters and no access modifiers.

C# Program Example

using System;

static class MathHelper
{
    public static int Factor;

    static MathHelper()
    {
        Console.WriteLine("Static Constructor Called");
        Factor = 10;
    }

    public static int Multiply(int x)
    {
        return x * Factor;
    }
}

class Program
{
    static void Main()
    {
        Console.WriteLine("Main Started");
        // Accessing a static member triggers the static constructor
        int result = MathHelper.Multiply(5);
        Console.WriteLine("Result: " + result);
    }
}

Named Arguments and Namespace Aliasing in C#

Named Arguments

A named argument in C# allows you to pass arguments to a method by specifying the parameter name, regardless of their position. Benefits include:

  • Improves code readability, especially for methods with many parameters.
  • Allows skipping optional parameters easily.

Alias for Namespace in C#

Sometimes two namespaces may have the same class names, causing conflicts. To resolve this, we can use a namespace alias, which provides a shorter or unique name for a namespace.

C# Program: Namespace Aliasing

using System;
using Project1 = System.Collections.Generic;
using Project2 = System.Text;

class Program
{
    static void Main()
    {
        Project1.List<string> names = new Project1.List<string>();
        names.Add("Aliasing example");

        Project2.StringBuilder sb = new Project2.StringBuilder();
        sb.Append("Hello from alias!");

        Console.WriteLine(names[0]);
        Console.WriteLine(sb.ToString());
    }
}

Static Binding and Binary Operator Overloading in C#

Static Binding (Early Binding)

Static Binding means the method call is resolved at compile time. It occurs with:

  • Non-virtual methods.
  • Overloaded methods.
  • Calls to static or private methods.

C# Program: Binary Operator Overloading (Complex Numbers)

This program demonstrates overloading the binary + and - operators to handle complex number arithmetic.

using System;

class Complex
{
    public int Real;
    public int Imag;

    public Complex(int r, int i)
    {
        Real = r;
        Imag = i;
    }

    // Overloading the addition operator
    public static Complex operator +(Complex c1, Complex c2)
    {
        return new Complex(c1.Real + c2.Real, c1.Imag + c2.Imag);
    }

    // Overloading the subtraction operator
    public static Complex operator -(Complex c1, Complex c2)
    {
        return new Complex(c1.Real - c2.Real, c1.Imag - c2.Imag);
    }

    public void Display()
    {
        Console.WriteLine($"{Real} + {Imag}i");
    }
}

class Program
{
    static void Main()
    {
        Complex c1 = new Complex(5, 4);
        Complex c2 = new Complex(2, 3);

        Complex sum = c1 + c2;
        Complex diff = c1 - c2;

        Console.Write("Sum: ");
        sum.Display();

        Console.Write("Difference: ");
        diff.Display();
    }
}

Purpose and Use Cases of Delegates in C#

A delegate is a type that represents references to methods with a specific signature. It is like a type-safe function pointer in C/C++.

Key Purposes of Delegates

  • Callback Mechanism: Allows calling methods passed as parameters.
  • Event Handling: Used extensively with events in .NET (e.g., handling a button click).
  • Encapsulation: Encapsulates method(s) to be called dynamically.
  • Multicasting: A delegate can hold references to multiple methods and call them sequentially (multicast delegate).

C# Concepts: Copy Constructor, Indexer, Lambda

a) Copy Constructor

A copy constructor is a special constructor in a class used to create a new object as a copy of an existing object.

  • Takes an object of the same class as a parameter.
  • Performs deep or shallow copying of fields.
class Student
{
    public string Name;

    // Copy Constructor
    public Student(Student s)
    {
        Name = s.Name;
    }
}

b) Indexer in C#

An indexer allows a class or struct to be accessed like an array using the [ ] brackets.

  • Defined using the this keyword.
  • Can include get, set, or both accessors.
  • Makes an object behave like a collection.
class Sample
{
    private int[] data = new int[5];

    public int this[int index]
    {
        get { return data[index]; }
        set { data[index] = value; }
    }
}

c) Lambda Expression in C#

A lambda expression is a concise way to represent anonymous methods using the => operator.

Syntax:

(parameters) => expression_or_statement_block
Func<int, int> square = x => x * x;
// Console.WriteLine(square(5)); // Output: 25

Use Cases: LINQ queries, delegates and events, and anonymous function calls.

Definition and Features of Managed Code in .NET

Managed Code is the code that is executed and managed by the Common Language Runtime (CLR) in the .NET framework.

Features of Managed Code

  • The CLR manages memory allocation, garbage collection, and security.
  • Provides type safety, ensuring that code accesses memory only in authorized ways.
  • Includes built-in exception handling mechanisms.
  • Enables cross-language interoperability.

Optional Parameters and Enumerations in C#

Optional Parameters

Optional parameters allow you to omit arguments in a method call. They must be assigned default values if not provided.

Syntax Example:

void Display(string name = "Default") { }

C# Program: Storing Values in Enumerations

using System;

enum Department { Computer, Physics, Mathematics, Management }

enum College { Science, Commerce, Arts }

class Program
{
    static void Main()
    {
        Department dept = Department.Computer;
        College college = College.Science;

        Console.WriteLine("Department: " + dept);
        Console.WriteLine("College: " + college);
    }
}

Value Types vs. Reference Types in C#

The primary difference lies in where the data is stored and how variables handle assignment and equality.

Reference Types

  • Storage: Stored on the heap. The variable holds a reference (memory address) to the object location.
  • Copying: Copying creates a new reference pointing to the same object instance.
  • Equality: Determined by reference (do they point to the same memory location?), unless overridden.
  • Inheritance: Reference types can inherit from other classes.
  • Performance: May have a performance overhead due to heap allocation and garbage collection.

Value Types

  • Storage: Stored on the stack (or inline within a reference type). The variable stores the value itself directly.
  • Copying: Copying creates a new instance with its own copy of the value (deep copy).
  • Equality: Determined by value (do they contain the same data?).
  • Inheritance: Value types cannot inherit from other types (they implicitly inherit from System.ValueType).
  • Performance: Generally have a smaller memory footprint and can be more performant for small data structures.

Interfaces and Achieving Multiple Inheritance in C#

What is an Interface?

An interface is a contract that defines method signatures, properties, events, or indexers without providing implementation. Classes or structs that implement the interface must provide the implementation for all its members.

Use Cases for Interfaces

  • Defining common behavior for different classes.
  • Achieving multiple inheritance of behavior (since C# does not support multiple inheritance of classes).

C# Program: Multiple Inheritance via Interfaces

using System;

interface IAnimal
{
    void Speak();
}

interface IPet
{
    void Play();
}

// A class can implement multiple interfaces
class Dog : IAnimal, IPet
{
    public void Speak()
    {
        Console.WriteLine("Dog barks");
    }

    public void Play()
    {
        Console.WriteLine("Dog plays fetch");
    }
}

class Program
{
    static void Main()
    {
        Dog d = new Dog();
        d.Speak();
        d.Play();
    }
}

The Role of Finalize() Method in C# Cleanup

The Finalize() method (implemented via the destructor syntax ~ClassName()) is used to perform cleanup operations on unmanaged resources before an object is reclaimed by the Garbage Collector (GC).

Key Points

  • It is automatically called by the GC, not explicitly by the user.
  • It is declared using destructor syntax: ~ClassName().
  • Its execution is non-deterministic and should only be used for releasing unmanaged resources. For deterministic cleanup, IDisposable should be used.

Example: Destructor Syntax

class Demo
{
    ~Demo()
    {
        Console.WriteLine("Finalize called.");
    }
}

C# Exception Handling Keywords and Examples

Keywords Used in Exception Handling

  • try: Block of code to monitor for potential exceptions.
  • catch: Handles the exception if one occurs in the try block.
  • finally: Executes regardless of whether an exception occurred or was handled.
  • throw: Manually throws an exception.

C# Program: Handling Specific Exceptions

This program demonstrates handling IndexOutOfRangeException and InvalidCastException.

using System;

class Program
{
    static void Main()
    {
        try
        {
            int[] arr = new int[3];
            // 1. IndexOutOfRangeException
            Console.WriteLine(arr[5]);

            // 2. InvalidCastException
            object obj = "Hello";
            int num = (int)obj;
        }
        catch (IndexOutOfRangeException e)
        {
            Console.WriteLine("Index Error: " + e.Message);
        }
        catch (InvalidCastException e)
        {
            Console.WriteLine("Cast Error: " + e.Message);
        }
        finally
        {
            Console.WriteLine("Finally block executed.");
        }
    }
}

LINQ Query Syntax and Standard Operators

LINQ Query Syntax

Query Syntax is a declarative, SQL-like syntax used in LINQ (Language Integrated Query) to query collections (like arrays, lists, or databases).

Basic Structure:

from variable in collection
where condition
orderby variable
select variable;

Standard Query Operators in LINQ

  • select: Projects (transforms) the elements of a sequence into a new form.
  • where: Filters a sequence based on a specified condition.
  • orderBy: Sorts the elements of a sequence in ascending or descending order.
  • Contains: (Method Syntax) Checks if an element exists in the collection.

C# Program: LINQ Operators Example

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 5, 10, 15, 20, 25 };

        // Using where, orderby, and select (Query Syntax)
        var result = from n in numbers
                     where n > 10
                     orderby n
                     select n;

        Console.WriteLine("Numbers > 10 (Ordered):");
        foreach (var num in result)
        {
            Console.WriteLine(num);
        }

        // Using Contains (Method Syntax)
        bool hasTwenty = numbers.Contains(20);
        Console.WriteLine("\nList contains 20? " + hasTwenty);
    }
}

Lambda Expressions: Definition and Types

A lambda expression is an anonymous function used to create delegates or expression tree types concisely.

Lambda Expression Types

  1. Expression Lambda

    Used when the body consists of a single expression that returns a value. The compiler infers the return type.

    // Example: Takes x, returns x * x
    Func<int, int> square = x => x * x;
  2. Statement Lambda

    Used when the body requires multiple statements. The statements must be enclosed in curly braces {}.

    // Example: Performs an action and returns a value
    Func<int, int> calculate = x =>
    {
        int temp = x + 10;
        return temp * 2;
    };
  3. Lambda with Parameters

    Lambdas can take zero, one, or multiple parameters. Parentheses are optional for a single parameter but required for zero or multiple parameters.

    // Zero parameters
    Action hello = () => Console.WriteLine("Hello");
    
    // Multiple parameters
    Func<int, int, int> add = (a, b) => a + b;