DEV Community

Cover image for 20 (Intermediate-Level) C# Interview Questions
ByteHide
ByteHide

Posted on • Originally published at bytehide.com

20 (Intermediate-Level) C# Interview Questions

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#:

public class Person
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

public class Person
{
    public string Name { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

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:

public static class Utility
{
    public static int Sum(params int[] values)
    {
        int sum = 0;
        foreach (int value in values)
        {
            sum += value;
        }
        return sum;
    }
}
Enter fullscreen mode Exit fullscreen mode

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

int result1 = Utility.Sum(1, 2, 3);
int result2 = Utility.Sum(1, 2, 3, 4, 5);
Enter fullscreen mode Exit fullscreen mode

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#:

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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

int sum = MathUtility.Add(1, 2);
int difference = MathUtility.Subtract(3, 1);
Enter fullscreen mode Exit fullscreen mode

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.
public partial class SecondForm : Form
{
    public string Data { get; set; }

    public SecondForm()
    {
        InitializeComponent();
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. In the first form, instantiate the second form and set the Data property with the data you want to pass.
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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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

    public void Drive()
    {
        Console.WriteLine("The car is driving.");
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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

Car car2 = new Car();
car2.Model = "Honda";
car2.Year = 2021;
Enter fullscreen mode Exit fullscreen mode

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#:

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!");
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

// 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.
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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.
    }
}
Enter fullscreen mode Exit fullscreen mode

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#:

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

Nullable<int> nullableInt = null;
int? anotherNullableInt = null;
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

C# also supports nullable value type conversions and null coalescing using the ?? operator to provide a default value when a nullable type is ‘null’:

int? a = null;
int b = a ?? 10; // b = 10, because a is null
Enter fullscreen mode Exit fullscreen mode

Describe the concept of LINQ in C#.

Answer

Language Integrated Query (LINQ) 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).
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
}
Enter fullscreen mode Exit fullscreen mode
  1. 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.
var evenNumbersMethod = numbers.Where(n => n % 2 == 0).OrderBy(n => n);

foreach (var n in evenNumbersMethod)
{
    Console.WriteLine(n); // 2, 4, 6
}
Enter fullscreen mode Exit fullscreen mode

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.
Func<int, int, int> sum = (a, b) => a + b;
int result = sum(1, 2); // result = 3
Enter fullscreen mode Exit fullscreen mode
  1. 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.
Func<int, int, int> sum = (a, b) => {
    int result = a + b;
    return result;
};
int result = sum(1, 2); // result = 3
Enter fullscreen mode Exit fullscreen mode

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.
public async Task<string> DownloadContentAsync(string url)
{
    using (HttpClient client = new HttpClient())
    {
        var content = await client.GetStringAsync(url);
        return content;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • 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.
public async Task ProcessDataAsync()
{
    var content = await DownloadContentAsync("https://example.com");
    Console.WriteLine(content);
}
Enter fullscreen mode Exit fullscreen mode

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.
Task.Run(() => {
    Console.WriteLine("Task-based operation running on a different thread");
});
Enter fullscreen mode Exit fullscreen mode
  • 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.
Task<int> task = Task.Run<int>(() => {
    return 42;
});
int result = task.Result; // waits for the task to complete and retrieves the result
Enter fullscreen mode Exit fullscreen mode
  • 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.
Parallel.For(0, 10, i => {
    Console.WriteLine($"Parallel loop iteration: {i}");
});
Enter fullscreen mode Exit fullscreen mode

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.
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // Covariant assignment
Enter fullscreen mode Exit fullscreen mode
  • 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.
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;
Enter fullscreen mode Exit fullscreen mode

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:
  var myString = "Hello, World!";
  int stringLength = myString.Length; // Early binding
Enter fullscreen mode Exit fullscreen mode
  • 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:

  dynamic someObject = GetSomeObject();
  someObject.DoSomething(); // Late binding
Enter fullscreen mode Exit fullscreen mode

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.

gacutil -i MyAssembly.dll
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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:

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

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

int itemCount = concurrentQueue.Count; // itemCount = 1000
Enter fullscreen mode Exit fullscreen mode

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:

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.");
}
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (2)

Collapse
 
subhmsft profile image
Subhasish G

Not only for interviews, but one could use this content for self-study as well. Perfect explanations for some of the essential concepts in C#.

Collapse
 
victordolzan profile image
Victor Dolsan • Edited

This is a incredible article that helps one to be prepared for a job interview as well as using It just for studying. Thanks for sharing