20 (Intermediate-Level) C# Interview Questions
Tags: csharp, dotnet, career, programming
Published: 2023-06-27 11:56:49

Hey, C# devs! Ready for a new challenge?

We’re tackling 20 intermediate-level C# interview questions to level up your skills and amp up your problem-solving game.

Think you can handle it?

Let’s jump in!

---

## How do you create a property in C#?

### Answer

In C#, properties are members of a class that provide a flexible mechanism to read, write, or compute the value of private fields. Properties can be created using `get` and `set` accessors in the property definition.

A property can have a `get` accessor, a `set` accessor, or both, depending on whether you want the property to be read-only, write-only, or read-write.

Here’s an example of a property with both `get` and `set` accessors in C#:

```csharp
public class Person
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}
```

In C# 3.0 and later, you can use auto-implemented properties, which enables you to create properties without having to define the backing field explicitly:

```csharp
public class Person
{
    public string Name { get; set; }
}
```

## What is the purpose of the ‘params’ keyword in C#?

### Answer

The `params` keyword in C# allows a method to accept a variable number of arguments of a specified type. Using the `params` keyword, you can pass an array of values or separate comma-separated values to the method. Inside the method, the `params` parameter is treated like an array.

Here’s an example of using the `params` keyword:

```csharp
public static class Utility
{
    public static int Sum(params int[] values)
    {
        int sum = 0;
        foreach (int value in values)
        {
            sum += value;
        }
        return sum;
    }
}
```

You can call the `Sum` method with any number of arguments, like this:

```csharp
int result1 = Utility.Sum(1, 2, 3);
int result2 = Utility.Sum(1, 2, 3, 4, 5);
```

## What are the characteristics of a static class in C#?

### Answer

In C#, a static class is a class that cannot be instantiated or inherited. It can only have static members, and it cannot have instance members (like instance properties, methods, or fields). The main characteristics of a static class are:

-   It is declared with the `static` keyword.
-   It cannot have a constructor (except the static constructor).
-   It cannot be instantiated or inherited.
-   All members of a static class must also be static.
-   It can be accessed directly using the class name, without creating an instance.

A common use of static classes is for utility functions and extension methods. Here’s an example of a static class in C#:

```csharp
public static class MathUtility
{
    public static int Add(int x, int y)
    {
        return x + y;
    }

    public static int Subtract(int x, int y)
    {
        return x - y;
    }
}
```

To use this static class, you simply call its methods like this:

```csharp
int sum = MathUtility.Add(1, 2);
int difference = MathUtility.Subtract(3, 1);
```

## How do you pass data between two forms in C#?

### Answer

In C#, there are several ways to pass data between two forms in a Windows Forms application. One common approach is to use public properties or methods to expose the data that needs to be passed. Here’s an example:

1.  Create a second form with a public property that will hold the data to be passed.

```csharp
public partial class SecondForm : Form
{
    public string Data { get; set; }

    public SecondForm()
    {
        InitializeComponent();
    }
}
```

2.  In the first form, instantiate the second form and set the `Data` property with the data you want to pass.

```csharp
public partial class FirstForm : Form
{
    public FirstForm()
    {
        InitializeComponent();
    }

    private void OpenSecondFormButton_Click(object sender, EventArgs e)
    {
        SecondForm secondForm = new SecondForm();
        secondForm.Data = "Data to be passed";
        secondForm.Show();
    }
}
```

In this example, when the user clicks the `OpenSecondFormButton`, the second form opens, and the `Data` property of the second form contains the data passed from the first form.

## What is the difference between a class and an object in C#?

### Answer

In C#, a class is a blueprint for creating objects. It defines the structure, properties, and methods that objects of that class will have. Classes are reference types, and they can inherit from other classes to extend or override functionality.

An object, on the other hand, is an instance of a class. It is created from the class definition and occupies memory when instantiated. Objects are instances of classes, and they contain the data (fields) and behaviors (methods) that are defined in the class.

For example, consider a `Car` class:

```csharp
public class Car
{
    public string Model { get; set; }
    public int Year { get; set; }

    public void Drive()
    {
        Console.WriteLine("The car is driving.");
    }
}
```

In this example, `Car` is the class, and it defines the structure and behavior of car objects. To create objects (instances) of the class, you use the `new` keyword, like this:

```csharp
Car car1 = new Car();
car1.Model = "Toyota";
car1.Year = 2020;

Car car2 = new Car();
car2.Model = "Honda";
car2.Year = 2021;
```

In this example, `car1` and `car2` are objects (instances) of the `Car` class, and they have their own individual sets of properties and methods.

## What is a delegate in C#?

### Answer

A delegate in C# is a type that defines a method signature and can hold a reference to a method with a matching signature. Delegates are similar to function pointers in C++, but they are type-safe and secure. They are commonly used for implementing events and callbacks.

Delegates can be used to pass methods as arguments, assign them to class properties, or store them in collections. Once a delegate is defined, it can be used to create delegate instances that point to methods with the same signature.

Here’s an example of a delegate in C#:

```csharp
public delegate void MyDelegate(string message);

public class MyClass
{
    public static void DisplayMessage(string message)
    {
        Console.WriteLine("Message: " + message);
    }
}

public class Program
{
    public static void Main()
    {
        // Create a delegate instance
        MyDelegate myDelegate = MyClass.DisplayMessage;

        // Invoke the method through the delegate
        myDelegate("Hello, World!");
    }
}
```

In this example, `MyDelegate` is a delegate that defines the method signature for methods that take a `string` argument and return `void`. The `Main` method creates a delegate instance, assigns the `MyClass.DisplayMessage` method to it, and invokes the method through the delegate.

## How do you implement polymorphism in C#?

### Answer

Polymorphism is one of the fundamental principles of object-oriented programming. It allows objects of different classes to be treated as objects of a common superclass. In C#, there are two types of polymorphism: compile-time (method overloading) and runtime (method overriding and interfaces).

Method overriding and interfaces are the common ways to implement runtime polymorphism. Here’s an example using method overriding:

```csharp
// Base class
public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("The animal speaks.");
    }
}

// Derived class
public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("The dog barks.");
    }
}

public class Program
{
    public static void Main()
    {
        Animal myAnimal = new Dog();
        myAnimal.Speak(); // Output: The dog barks.
    }
}
```

In this example, we define a base class `Animal` and a derived class `Dog`. The `Speak` method in the `Animal` class is marked as `virtual`, allowing its behavior to be overridden by derived classes.

The `Dog` class overrides the `Speak` method with its own implementation. When we create a `Dog` object and assign it to an `Animal` variable, the overridden method in the `Dog` class is called at runtime, exhibiting polymorphism.

To implement polymorphism via interfaces, you can define an interface and then have classes implement the interface methods. For example:

```csharp
public interface ISpeak
{
    void Speak();
}

public class Dog : ISpeak
{
public void Speak()
    {
        Console.WriteLine("The dog barks.");
    }
}

public class Cat : ISpeak
{
    public void Speak()
    {
        Console.WriteLine("The cat meows.");
    }
}

public class Program
{
    public static void Main()
    {
        ISpeak myAnimal = new Dog();
        myAnimal.Speak(); // Output: The dog barks.

        myAnimal = new Cat();
        myAnimal.Speak(); // Output: The cat meows.
    }
}
```

In this example, we define an `ISpeak` interface with a `Speak` method. The `Dog` and `Cat` classes implement this interface and provide their own implementation of the `Speak` method. We can create and assign objects of these classes to an `ISpeak` variable, and the appropriate implementation of the `Speak` method is called at runtime, demonstrating polymorphism.

## What is a struct in C#?

### Answer

A struct (short for “structure”) in C# is a value type that can contain fields, properties, methods, and other members like a class.

However, unlike classes, which are reference types, structs are value types and are stored on the stack. Structs do not support inheritance (except for implicitly inheriting from System.ValueType), and they cannot be used as a base for other classes or structs.

Structs are useful for representing small, lightweight objects that have a small memory footprint and do not require garbage collection overhead. They are commonly used to represent simple data structures, such as points in a 2D space, dates, time intervals, and colors.

Here’s an example of a struct in C#:

```csharp
public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public double DistanceToOrigin()
    {
        return Math.Sqrt(X * X + Y * Y);
    }
}

public class Program
{
    public static void Main()
    {
        Point p = new Point(3, 4);
        Console.WriteLine("Distance to origin: " + p.DistanceToOrigin()); // Output: Distance to origin: 5
    }
}
```

In this example, we define a `Point` struct with properties `X` and `Y`, a constructor, and a method `DistanceToOrigin`. We can create `Point` instances and use their methods like we would with class instances.

## What is the difference between ‘throw’ and ‘throw ex’ in exception handling?

### Answer

In C#, when handling exceptions, it’s important to understand the difference between `throw` and `throw ex`:

-   `throw`: When you catch an exception and want to rethrow the original exception, you use the `throw` statement without specifying any exception object. This preserves the original exception’s stack trace and allows the upstream exception handler to access the full stack trace.
-   `throw ex`: When you catch an exception and want to throw a new or modified exception object, you use the `throw ex` statement, where `ex` is an instance of an exception. This replaces the original exception’s stack trace, and the upstream exception handler will only see the stack trace starting from the point where `throw ex` was called.

Here’s an example:

```csharp
public class Example
{
    public void Method1()
    {
        try
        {
            // Code that might raise an exception
        }
        catch (Exception ex)
        {
            // Logging or other exception handling code

            // Rethrow the original exception
            throw;
        }
    }

    public void Method2()
    {
        try
        {
            // Code that might raise an exception
        }
        catch (Exception ex)
        {
            // Logging or other exception handling code

            // Rethrow a modified exception
            throw new ApplicationException("An error occurred in Method2.", ex);
        }
    }
}
```

In `Method1`, we catch an exception and rethrow it using `throw`. The upstream exception handler can access the original stack trace of the exception. In `Method2`, we catch an exception and throw a new `ApplicationException` with the original exception as its inner exception using `throw ex`. The upstream exception handler will see the stack trace starting from the point where `throw ex` was called.

## What is a nullable type and how is it used in C#?

### Answer

Nullable types in C# are value types that can have a ‘null’ value. By default, value types cannot have a ‘null’ value, as they have a default value (like 0 for int, false for bool, etc.). To create a nullable type, you can use the built-in `Nullable<T>` structure or the syntactic sugar `?` modifier.

Nullable types are useful when you need to indicate the absence of a value or when working with data sources (like databases) that might contain missing values.

Creating nullable types:

```csharp
Nullable<int> nullableInt = null;
int? anotherNullableInt = null;
```

Nullable types can be checked for having a value using the `HasValue` property and retrieved using the `Value` property. Alternatively, you can use the `GetValueOrDefault` method to get the value if it exists or a default value:

```csharp
int? x = null;
if (x.HasValue)
{
    Console.WriteLine("x has a value of: " + x.Value);
}
else
{
    Console.WriteLine("x is null"); // This will be printed
}

int y = x.GetValueOrDefault(-1); // y = -1, because x is null
```

C# also supports nullable value type conversions and [null coalescing](https://www.bytehide.com/blog/null-coalescing-operator-csharp/ "Null Coalescing Operator ($) in C#: Full Guide") using the `??` operator to provide a default value when a nullable type is ‘null’:

```csharp
int? a = null;
int b = a ?? 10; // b = 10, because a is null
```

## Describe the concept of LINQ in C#.

### Answer

Language Integrated Query ([LINQ](https://www.bytehide.com/blog/linq-csharp/ "Mastering C# LINQ Guide: From Beginner and Expert")) is a feature introduced in C# 3.0 (.NET Framework 3.5) that provides a set of query operators and a unified query syntax for querying, filtering, and transforming data from various types of data sources like collections, XML, databases, or even custom sources.

LINQ allows you to write type-safe, powerful, and expressive queries directly in C# with the benefits of full support from the compiler, static typing, and IntelliSense.

LINQ offers two types of syntax for writing queries:

1.  **Query Syntax**: This is a more SQL-like, declarative syntax that provides a high level of abstraction and readability. Query syntax requires the `from` keyword (retrieves the data), `select` keyword (specifies the output), and optional keywords (such as `where`, `group`, or `orderby`).

```csharp
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

var evenNumbersQuery = from n in numbers
                        where n % 2 == 0
                        orderby n
                        select n;

foreach (var n in evenNumbersQuery)
{
    Console.WriteLine(n); // 2, 4, 6
}
```

2.  **Method Syntax**: This is based on LINQ extension methods of the `System.Linq.Enumerable` class and uses lambda expressions to manipulate the data. Method syntax provides a more functional approach but may be less readable if the query is complex.

```csharp
var evenNumbersMethod = numbers.Where(n => n % 2 == 0).OrderBy(n => n);

foreach (var n in evenNumbersMethod)
{
    Console.WriteLine(n); // 2, 4, 6
}
```

LINQ supports a variety of providers like LINQ to Objects (for in-memory collections), LINQ to XML (for XML documents), and LINQ to Entities (for entity framework), enabling developers to work with different data sources using a similar query syntax.

## What is a lambda expression in C#?

### Answer

A lambda expression in C# is an anonymous function that is used to create inline delegates, expression trees, and expressions for LINQ queries or other functional programming constructs. Lambda expressions rely on the `=>` operator (called the lambda operator) to separate the input parameters and the expression or statement block.

Lambda expressions can be used for both expression and statement lambdas:

1.  **Expression Lambda**: This consists of an input parameter list, followed by the lambda operator, and a single expression. An expression lambda automatically returns the result of the expression without the need for an explicit `return` statement.

```csharp
Func<int, int, int> sum = (a, b) => a + b;
int result = sum(1, 2); // result = 3
```

2.  **Statement Lambda**: This is similar to an expression lambda but contains a statement block enclosed in braces `{}` instead of a single expression. A statement lambda allows for multiple statements and requires an explicit `return` statement if a value must be returned.

```csharp
Func<int, int, int> sum = (a, b) => {
    int result = a + b;
    return result;
};
int result = sum(1, 2); // result = 3
```

Lambda expressions are a concise and efficient way of representing delegates or expressions, and are commonly used with LINQ queries, event handlers, or higher-order functions that expect a function as an argument.

## Describe the async and await keywords in C#. How can they be used together?

### Answer

In C#, the `async` and `[await](https://www.bytehide.com/blog/await-csharp/ "Mastering Await in C#: From Zero to Hero")` keywords are used to write asynchronous code that can run concurrently without blocking the main execution thread. This allows for more efficient and responsive applications, particularly in cases of I/O-bound operations (e.g., calling web services, reading files, or working with databases) or CPU-bound operations that can be parallelized.

-   **async**: The `async` keyword is added to a method to indicate that it’s an asynchronous method. An async method usually returns a `Task` for void-returning methods or `Task<TResult>` for methods that return a value of type TResult. An async method can contain one or more `await` expressions.

```csharp
public async Task<string> DownloadContentAsync(string url)
{
    using (HttpClient client = new HttpClient())
    {
        var content = await client.GetStringAsync(url);
        return content;
    }
}
```

-   **await**: The `await` keyword is used to pause the execution of an async method until an awaited task completes. It asynchronously waits for the task to complete and returns the result (if any) without blocking the main execution thread. The `await` keyword can only be used inside an async method.

```csharp
public async Task ProcessDataAsync()
{
    var content = await DownloadContentAsync("https://example.com");
    Console.WriteLine(content);
}
```

When using `async` and `await` together, ensure that you propagate the asynchronous behavior up the call stack by marking every calling method with the `async` keyword and awaiting async methods in the method body. This “async all the way” approach ensures that the benefits of asynchronous programming are fully utilized throughout the application.

## What is the Task Parallel Library (TPL) and how does it relate to C#?

### Answer

The Task Parallel Library (TPL) is a set of APIs introduced in the .NET Framework 4.0 to simplify parallelism, concurrency, and asynchronous programming. It is part of the `System.Threading.Tasks` namespace and provides a higher-level, more abstract model for working with multithreading, asynchrony, and parallelism than the traditional lower-level threading constructs (e.g., ThreadPool or Thread class).

TPL’s main components include `Task`, `Task<TResult>`, and `Parallel` classes, which serve the following purposes:

-   **Task**: Represents an asynchronous operation that doesn’t return a value. Tasks can be awaited using the `await` keyword, or you can attach continuations to them using the `ContinueWith` method.

```csharp
Task.Run(() => {
    Console.WriteLine("Task-based operation running on a different thread");
});
```

-   **Task**: Represents an asynchronous operation that returns a value of type TResult. It derives from the `Task` class and has a `Result` property that holds the result once the task is complete.

```csharp
Task<int> task = Task.Run<int>(() => {
    return 42;
});
int result = task.Result; // waits for the task to complete and retrieves the result
```

-   **Parallel**: Provides parallel versions of `for` and `foreach` loops, as well as an `Invoke` method to execute multiple actions concurrently. The `Parallel` class can automatically utilize multiple CPU cores or threads, allowing you to parallelize CPU-bound operations easily.

```csharp
Parallel.For(0, 10, i => {
    Console.WriteLine($"Parallel loop iteration: {i}");
});
```

The TPL allows developers to write efficient and scalable parallel, concurrent, and asynchronous code in C# without having to deal directly with low-level threading and synchronization primitives.

## Can you explain covariance and contravariance in C#?

### Answer

Covariance and contravariance are terms related to the type relationships in C# when dealing with generics, delegates, or arrays. They describe how a derived type can be used in place of a base type (or vice versa) when assigning variables, parameters, or return values.

-   **Covariance** (from less derived type to more derived type): Covariance allows you to use a more derived type instead of the original generic type parameter. In C#, covariance is supported for arrays, interfaces with generic parameters, and delegates with matching return types.

```csharp
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // Covariant assignment
```

-   **Contravariance** (from more derived type to less derived type): Contravariance allows you to use a less derived type instead of the original generic type parameter. In C#, contravariance is supported for interfaces with generic parameters marked with the `in` keyword and delegates with matching input parameters.

```csharp
IComparer<object> objectComparer = Comparer<object>.Default;
IComparer<string> stringComparer = objectComparer; // Contravariant assignment

// Using contravariant delegate
Action<object> setObject = obj => Console.WriteLine(obj);
Action<string> setString = setObject;
```

Covariance and contravariance can help increase the flexibility and reusability of your code by allowing assignments and conversions between different type hierarchies without explicit type casting.

## Describe the differences between early binding and late binding in C#.

### Answer

Early binding and late binding are two mechanisms in C# related to how the compiler and runtime resolve types, methods, or properties during program execution.

-   **Early Binding**: Also known as static or compile-time binding, early binding refers to the process of resolving types, methods, or properties at compile time. With early binding, the compiler validates that the members being called exist and that they’re being called correctly regarding their accessibility, parameters, and return values. Early binding provides benefits like better performance (due to reduced runtime overhead) and type safety, as the compiler can catch and report errors during compilation. Example:

```csharp
  var myString = "Hello, World!";
  int stringLength = myString.Length; // Early binding
```

-   **Late Binding**: Also known as dynamic or runtime binding, late binding refers to the process of resolving types, methods, or properties at runtime. With late binding, the actual binding of members occurs only when the code is executed, which can lead to runtime errors if the members don’t exist or are not accessible. 

Example:

```csharp
  dynamic someObject = GetSomeObject();
  someObject.DoSomething(); // Late binding
```

Late binding is often used when the exact types or members aren’t known at compile time or when working with COM objects, reflection, or dynamic types. Late binding has certain advantages, such as allowing for more flexibility, extensibility, and the ability to work with types that aren’t known at compile time.

However, it also comes with disadvantages such as increased runtime overhead (due to additional type checks, method lookups, or dynamic dispatch) and a loss of type safety (due to the possibility of runtime errors).



## What is the Global Assembly Cache (GAC) in C#?

### Answer

The Global Assembly Cache (GAC) is a centralized repository or cache for storing and sharing .NET assemblies (DLLs) on a machine. The GAC is part of the .NET Framework and is used to avoid DLL conflicts and enable side-by-side execution of different versions of the same assembly.

Assemblies stored in the GAC must have a strong name, which consists of the assembly name, version number, culture information (if applicable), and a public key token (generated from the developer’s private key). This strong name allows the GAC to uniquely identify and manage each assembly and its versions.

To install an assembly in the GAC, you can use the `gacutil` utility, or you can drag and drop the assembly into the GAC folder using Windows Explorer.

```csharp
gacutil -i MyAssembly.dll
```

As a developer, you typically don’t need to explicitly reference assemblies from the GAC, as the .NET runtime automatically looks for them in the GAC before it searches other locations. However, it’s essential to be aware of the GAC and understand how it impacts assembly sharing, versioning, and deployment scenarios.

## Explain how the ‘var’ keyword works in C#, and give a practical example of its use.

### Answer

In C#, the `var` keyword is used to implicitly type a local variable when you don’t need or want to explicitly specify the type. When you use the `var` keyword, the C# compiler infers the type of the variable based on the value assigned to it during initialization. The variable is still strongly-typed at compile-time, just like any other typed variable.

It’s important to note that the `var` keyword can only be used with local variables, and the variable must be initialized with a value during its declaration (otherwise, the compiler cannot infer the type).

Using the `var` keyword can provide benefits like increased readability and ease of refactoring, particularly when dealing with complex or verbose types (e.g., generics or anonymous types).

Example:

```csharp
var number = 42; // int
var message = "Hello, World!"; // string
var collection = new List<string>(); // List<string>
var anonymousType = new { Name = "John", Age = 30 }; // Anonymous type
```

In the example above, the compiler infers the types of the variables based on their assigned values. Using `var` here can make the code more concise and readable.

However, it’s important to strike the right balance between readability and maintainability. In cases where using `var` might decrease the readability or understandability of the code, it’s better to use an explicit type.

## What is a thread-safe collection, and can you give an example of one in C#?

### Answer

A thread-safe collection is a data structure designed to be safely and correctly accessed or modified by multiple threads concurrently. In general, most built-in .NET collection classes (e.g., `List<T>`, `Dictionary<TKey, TValue>`, etc.) are not thread-safe, which means that concurrent access or modifications can lead to unexpected results or data corruption.

To provide thread-safe collections, the .NET Framework offers the `System.Collections.Concurrent` namespace, which includes several thread-safe collection classes, such as:

-   `ConcurrentBag<T>`: An unordered collection of items that allows duplicates and has fast `Add` and `Take` operations.
-   `ConcurrentQueue<T>`: A first-in, first-out (FIFO) queue that supports concurrent `Enqueue` and `TryDequeue` operations.
-   `ConcurrentStack<T>`: A last-in, first-out (LIFO) stack that supports concurrent `Push` and `TryPop` operations.
-   `ConcurrentDictionary<TKey, TValue>`: A dictionary that supports thread-safe `Add`, `Remove`, and other dictionary operations.

Example:

```csharp
ConcurrentQueue<int> concurrentQueue = new ConcurrentQueue<int>();

Parallel.For(0, 1000, i => {
    concurrentQueue.Enqueue(i);
});

int itemCount = concurrentQueue.Count; // itemCount = 1000
```

In the example above, the `ConcurrentQueue<int>` instance safely handles concurrent `Enqueue` operations without the need for manual locking or synchronization.

Keep in mind that using thread-safe collections can come with some performance overhead, so you should carefully evaluate the trade-off between thread-safety and performance in your specific use case.

## Explain how exception handling works in C# using try-catch-finally blocks.

### Answer

Exception handling in C# is a mechanism for dealing with unexpected or exceptional conditions that may occur during program execution. It helps to maintain the normal flow of the program and ensure that resources are properly released even when an exception is encountered.

In C#, exception handling is primarily done using the `try`, `catch`, and `finally` blocks:

-   **try**: The `try` block contains the code that might throw an exception. You enclose the code that may raise exceptions within this block.
-   **catch**: The `catch` block is used to handle the specific exception that is thrown within the `try` block. You can have multiple `catch` blocks for different exception types. The first matching `catch` block that can handle the exception type will be executed.
-   **finally**: The `finally` block is used to execute code regardless of whether an exception was thrown or not. This block is optional, and it’s typically used for resource cleanup (e.g., closing files or database connections).

Example:

```csharp
try
{
    int result = divide(10, 0);
}
catch (DivideByZeroException ex)
{
    Console.WriteLine($"Caught DivideByZeroException: {ex.Message}");
}
catch (Exception ex)
{
    Console.WriteLine($"Caught a generic exception: {ex.Message}");
}
finally
{
    Console.WriteLine("This will execute, no matter what.");
}
```

In the example above, the `try` block attempts to execute a division operation that throws a `DivideByZeroException`. The `catch` block handling `DivideByZeroException` is executed, and the exception message is written to the console. Regardless of the exception, the `finally` block will always execute, ensuring that any necessary cleanup is performed.

---

There you go — 20 intermediate-level C# questions! Don’t forget, our article series covers C# Interview Questions and Answers for all skill levels.

Just follow our content to stay in the know and tackle more C# challenges. Keep on coding, everyone!