20 C# Interview Questions (for Experienced) 2023
Tags: csharp, dotnet, programming, career
Published: 2023-06-28 14:21:46

Hey C# Developer! Do you know where you are? Yes, on Dev.to but more specifically. That’s right! You’re in part 4 of the most comprehensive C# Interview Questions and Answers article series!

Little more to say if you’re coming from a previous article. As I said, let’s take the difficulty level up a notch and without further blah blah, let’s get started!

## What are the differences between IEnumerable, ICollection, and IList interfaces in C#?

### Answer

IEnumerable, ICollection, and IList are interfaces provided by the .NET framework libraries for working with collections of data. They have the following differences:

**IEnumerable**:

-   Provides the basic functionality to iterate through a collection using a foreach loop.
-   It supports only-read access.
-   Ideal for situations where you only need to iterate through a collection.
-   Defines only one method: GetEnumerator(), which returns an IEnumerator.

**ICollection**:

-   Inherits from IEnumerable.
-   Represents a collective group of elements with the addition of functionalities like size, enumeration, and synchronization.
-   Adds basic collection manipulation like Add, Remove, and Clear.
-   Supports both read and write access.
-   Provides properties like Count and IsReadOnly.
-   Provides methods like Add, Remove, Clear, and Contains.

**IList**:

-   Inherits from ICollection.
-   Represents a list of items that are accessible by index.
-   Provides advanced collection manipulation like insert and remove by index.
-   Supports random access to elements.
-   Contains properties like Item (property to access elements by index) and methods like Insert and RemoveAt.

## Explain the concept of partial classes in C#.

### Answer

A partial class, denoted with the `partial` keyword in C#, is a class whose definition can be split into multiple separate class definitions in the same namespace. All the separate class definitions with the `partial` keyword are combined by the compiler into a single class at the compile-time. Partial classes can be used to logically organize a large class or to enhance classes generated by code generators (e.g., for UI or web services).

Advantages of partial classes:

-   Provides better maintainability by dividing a large class into multiple logical files.
-   Enhances code generation in situations when you need to add your own code to a generated class.
-   Allows multiple developers to work on the same class simultaneously without conflicts.

Syntax:

```csharp
// File1.cs
public partial class MyClass
{
    public void Method1() { /* implementation */ }
}

// File2.cs
public partial class MyClass
{
    public void Method2() { /* implementation */ }
}
```

At compile-time, these two files are combined into a single class.

## What is boxing and unboxing in C#? Provide examples.

### Answer

Boxing and unboxing are operations in C# related to type conversions between value types and reference types.

**Boxing** is the process of converting a value type to a reference type (System.Object) by wrapping the value type inside a new object and storing it on the heap. The conversion is [implicit](https://www.bytehide.com/blog/csharp-implicit-conversion/ "C# Implicit Conversions: Techniques and Best Practices").  
Example:

```csharp
int value = 42;
object boxed = value; // Boxing
```

**Unboxing** is the process of converting a reference type back to a value type by extracting the value from the object and storing it on the stack, after checking its type is compatible. Unboxing requires an explicit type cast.  
Example:

```csharp
object boxed = 42;
int value = (int)boxed; // Unboxing
```

Note that these operations come with a performance cost as they involve memory allocation and deallocation, particularly when dealing with large data structures or frequent conversions.

## How can you implement multithreading in C#?

### Answer

Multithreading is a technique for executing multiple threads concurrently to improve performance and responsiveness in applications. In C#, there are several ways to implement multithreading, including:

1.  Using the `Thread` class from the `System.Threading` namespace. Creating a new Thread requires passing a method (delegate) as a parameter to its constructor that will be executed when the thread starts.

```csharp
using System.Threading;
class Program
{
    static void Main()
    {
        Thread thread = new Thread(new ThreadStart(PrintNumbers));
        thread.Start();
        PrintNumbers();
    }

    static void PrintNumbers()
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine(i);
        }
    }
}
```

2.  Using `Task` and `Task<TResult>` classes from `System.Threading.Tasks`. Tasks provide better abstraction and integration with the ThreadPool, eliminating the need to manually manage threads and their resources.

```csharp
using System.Threading.Tasks;
class Program
{
    static void Main()
    {
        Task task = new Task(PrintNumbers);
        task.Start();
        PrintNumbers();
        task.Wait(); // Optionally wait for the task to complete
    }

    static void PrintNumbers()
    {
        // Same as example 1
    }
}
```

3.  Using the `async` and `await` keywords for asynchronous programming. This approach simplifies working with long-running methods without blocking the calling thread.

```csharp
class Program
{
    static async Task Main()
    {
        Task longRunningTask = LongRunningOperationAsync();
        // Do other work while the long-running task completes
        await longRunningTask;
    }

    static async Task LongRunningOperationAsync()
    {
        await Task.Delay(5000); // Placeholder for a long-running operation
    }
}
```

Additionally, you can use `Parallel` class for parallel operations on collections (e.g., `Parallel.ForEach` or `Parallel.For`), and `ThreadPool` for managing a pool of threads.

## What is the concept of anonymous methods in C#?

### Answer

Anonymous methods in C# are a way to define inline unnamed methods that can be assigned to delegate instances. They provide a concise way to create small functions without declaring an entire named method. Anonymous methods use the `delegate` keyword and do not require a separate method declaration.

Example:

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

class Program
{
    static void Main()
    {
        DisplayMessage display = delegate(string message) // Anonymous method
        {
            Console.WriteLine(message);
        };

        display("Hello, Anonymous method!");
    }
}
```

In C# 3.0 and later, anonymous methods can be replaced by lambda expressions, which provide a more concise syntax. The previous example can be rewritten as:

```csharp
DisplayMessage display = message => Console.WriteLine(message);
```

## What are the different types of delegates in C#?

### Answer

Delegates in C# can be divided into three main types:

1.  **Singlecast Delegates**: These delegates reference a single method with a matching signature. When the delegate is invoked, it calls the referenced method.

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

class Program
{
    static void Main()
    {
        DisplayMessage display = ShowMessage;
        display("Hello, Singlecast delegate!");
    }

    static void ShowMessage(string message)
    {
        Console.WriteLine(message);
    }
}
```

2.  **Multicast Delegates**: These delegates can reference multiple methods with a matching signature. When the delegate is invoked, it calls all the referenced methods in the order they were added. Multicast delegates are created using the `+=` or `-=` operators.

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

class Program
{
    static void Main()
    {
        DisplayMessage display = ShowMessage1;
        display += ShowMessage2;
        display("Hello, Multicast delegate!");
    }

    static void ShowMessage1(string message)
    {
        Console.WriteLine("Message 1: " + message);
    }

    static void ShowMessage2(string message)
    {
        Console.WriteLine("Message 2: " + message);
    }
}
```

3.  **Generic Delegates**: These delegates use generic type parameters, allowing them to work with multiple types without casting or boxing/unboxing. C# provides three built-in generic delegates: `Func`, `Action`, and `Predicate`.

-   `Func<TResult>`: A delegate that represents a function with a return type.
-   `Action`: A delegate that represents a void-returning method with no parameters.
-   `Predicate<T>`: A delegate that represents a method that takes an input parameter and returns a boolean.

Example using `Func` and `Action`:

```csharp
class Program
{
    static void Main()
    {
        Func<int, int, int> add = (x, y) => x + y;
        Action<string> display = message => Console.WriteLine(message);

        int sum = add(10, 20);
        display($"Sum: {sum}");
    }
}
```

## Explain the principle of encapsulation in C#.

### Answer

Encapsulation is a fundamental object-oriented programming principle that refers to the bundling of data (properties) and behavior (methods) within a single class or object. This concept aims to:

-   Hide internal implementation details from other parts of the application (encapsulating inner workings).
-   Expose a well-defined public interface for communication between objects.
-   Easily update or modify the implementation without affecting the external callers.

In C#, encapsulation can be achieved using access modifiers (public, private, protected, internal, protected internal) to restrict access to the class members and by combining fields, properties, and methods inside a class.

Example:

```csharp
class BankAccount
{
    private double _balance; // Private field representing the internal state

    public double Balance // Public property exposing the balance with read-only access
    {
        get { return _balance; }
    }

    public void Deposit(double amount) // Public method for depositing funds
    {
        if (amount > 0)
        {
            _balance += amount;
        }
    }

    public void Withdraw(double amount) // Public method for withdrawing funds
    {
        if (amount > 0 && _balance >= amount)
        {
            _balance -= amount;
        }
    }
}
```

## What is the benefit of using an extension method in C#?

### Answer

Extension methods in C# are a way to add new methods to an existing type without modifying the original type, inheriting from it, or using any other type of customization. Extension methods can be useful in situations where you do not have the source code of the type, or you want to extend a sealed (non-inheritable) class. The benefits of using extension methods are:

-   Improved code readability and maintainability.
-   The ability to extend a class without modifying its code.
-   Adding functionality to a class without inheriting from it or using composition.

An extension method is defined as a static method in a static class and uses the `this` keyword before the first parameter to specify the type it is extending. Example:

```csharp
public static class StringExtensions
{
    public static string Reverse(this string input)
    {
        char[] chars = input.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
}

class Program
{
    static void Main()
    {
        string text = "Hello, extension method!";
        string reversedText = text.Reverse();

        Console.WriteLine(reversedText);
    }
}
```

Here, the `Reverse()` extension method is added to the `string` type and can be called as if it was a native method of the `string` class.

## How does C# support operator overloading?

### Answer

Operator overloading in C# allows any custom class or struct to redefine the behavior of certain operators (like `+`, `-`, `*`, `==`, etc.) for its instances. This can provide a more intuitive and natural syntax when working with objects of the custom type.

To overload an operator, you need to define a `public static` method in your class with the `operator` keyword followed by the operator symbol being overloaded. The method must have the required parameters and return type as specified by the operator being overloaded.

Example:

```csharp
public class ComplexNumber
{
    public double Real { get; set; }
    public double Imaginary { get; set; }

    public ComplexNumber(double real, double imaginary)
    {
        Real = real;
        Imaginary = imaginary;
    }

    // Overloading the + operator
    public static ComplexNumber operator +(ComplexNumber a, ComplexNumber b)
    {
        return new ComplexNumber(a.Real + b.Real, a.Imaginary + b.Imaginary);
    }
}

class Program
{
    static void Main()
    {
        ComplexNumber a = new ComplexNumber(3, 2);
        ComplexNumber b = new ComplexNumber(1, 4);

        ComplexNumber sum = a + b; // Uses the overloaded + operator
    }
}
```

## What is the use of the ‘base’ keyword in C#?

### Answer

The `base` keyword in C# is a reference to a class’s immediate parent class (base class) and is used to access its members, including properties, methods, and constructors. The `base` keyword is often used in the following scenarios:

1.  **Calling base class constructors**: When creating an instance of a derived class, you can call a base class constructor from the derived class constructor using the `base` keyword followed by the constructor parameters.

```csharp
public class Animal
{
    public int Legs { get; set; }

    public Animal(int legs)
    {
        Legs = legs;
    }
}

public class Dog : Animal
{
    public Dog() : base(4)
    {
        // Base class constructor is called with the argument 4
    }
}
```

2.  **Accessing base class methods**: In a derived class, you may want to access the original implementation of an overridden or shadowed method from the base class. In this case, you can use the `base` keyword followed by the method name.

```csharp
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("The animal makes a sound");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        // Call base class method
        base.MakeSound();
        Console.WriteLine("The dog barks");
    }
}

class Program
{
    static void Main()
    {
        Dog dog = new Dog();
        dog.MakeSound(); // Outputs: "The animal makes a sound" followed by "The dog barks"
    }
}
```

By using the `base` keyword, you can extend or reuse functionality from the base class while maintaining the inheritance relationship between the classes.

## Explain how LINQ queries work in C# and give an example.

### Answer

Language Integrated Query (LINQ) is a powerful feature in C# that allows you to query data from various data sources directly from within the C# language.

In LINQ, query operations are performed using familiar language syntax. This makes the query quite readable and easy to understand. LINQ queries can work with anything that implements the IEnumerable interface, such as arrays and collections. Plus, they are statically typed and use IntelliSense, making writing queries error-free.

Here is an example.

```csharp
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
IEnumerable<int> evenNumbers = from num in numbers where num % 2 == 0 select num;
```

In the above example, `evenNumbers` will hold 2, 4, 6, and 8. The query determines the numbers in the input sequence that fulfill the condition (num % 2 == 0), which are even numbers.

## What is a Thread Pool in C#? How can it be beneficial than creating a new thread?

### Answer

A Thread Pool in C# is a collection of threads that are made available for use by various tasks. Instead of each task creating, starting, and stopping a new thread, tasks can use a thread from the thread pool, greatly improving efficiency.

There are several benefits to using a thread pool:

-   Threads in a thread pool are reused, avoiding the overhead of creating a new thread every time.
-   The number of threads in the application does not spike up, avoiding the issue of too many threads which can slow down the system.
-   The system automatically manages the details of how and when tasks are assigned to threads in the thread pool.

Here is how you can use the ThreadPool:

```csharp
ThreadPool.QueueUserWorkItem(new WaitCallback(SomeMethod));
```

In the above line, `SomeMethod` is a method that is executed on a separate thread.

## What is a Nullable type in C# and how do you use it?

### Answer

In C#, Nullable types are value types which can take null values. This capability is crucial because the value types don’t normally take null values. A Nullable type is declared using the `?` symbol after the value type.

For instance, `int?` is a Nullable type based on the `int` type. You can assign any value which an `int` can take, plus an additional null value.

Here is how you can use it:

```csharp
int? number = null;
```

In the code above, because `int?` is a nullable type, we’re able to assign a `null` value to `number`.

## How does C# manage memory automatically?

### Answer

Memory management in C# is handled through the Garbage Collector (GC), an automatically enabled feature in the .NET framework. Here is how GC works:

-   When an object is created in .NET, it is stored in the heap memory.
-   The GC automatically reclaims the memory taken up by the unused objects, or the ones that are no longer being used by the application.
-   The GC does this automatically without the programmer having to write any specific code for it. This automatic feature saves the developer from various potential errors like memory leaks and invalid memory references.
-   The GC also compacts the space, thus enhancing memory locality, which in turn increases performance since objects that are referenced frequently end up being located close each other.

## How do you use reflection in C# to inspect an object’s properties and methods?

### Answer

Reflection in C# is a feature that enables you to obtain type information at runtime, inspect the metadata of types, create and manipulate instances of types, and invoke methods.

Here’s an example of using reflection to get an object’s properties:

```csharp
Person person = new Person();
Type type = person.GetType();
PropertyInfo[] properties = type.GetProperties();

foreach(PropertyInfo property in properties)
{
    Console.WriteLine("Name: {0}, Value: {1}", property.Name, property.GetValue(person, null));
}
```

In the example above, `GetType()` gets the type of the person object, `GetProperties()` gets all the properties of that type, then we loop through the properties and for each property name and its value, we write it out to the console.

## Can you explain what a Lambda expression is and give an example of its use?

### Answer

A lambda expression in C# is an anonymous function or method that can be used wherever delegate types are required. They provide a simple and concise way of defining methods. Lambda expressions use the `=>` operator, which can be read as “goes to”.

For example, here is a lambda expression that squares a number:

```csharp
Func<int, int> square = x => x * x;
```

Here `x` is the input parameter (can be more than one, comma separated) and `x * x` is the method body that is returned from the function.

Lambda expressions can be used in a lot of cases, but they are specially useful in LINQ queries, event handling and creating expression trees.

## How do you enforce thread safety in C#?

### Answer

Thread safety ensures that data shared between threads are shielded from corruption due to the threads being executed in an overlapping manner. In C#, you can enforce thread safety by using various techniques:

-   **Locks**: The `lock` keyword can be used to ensure that a block of code is not executed by more than one thread at the same time.

```csharp
private static object _lock = new object();

public void UpdateCounter()
{
    lock(_lock)
    {
        counter++;
    }
}
```

-   **Monitor**: `Monitor` class provides exclusive lock similar to `lock` keyword.

```csharp
Monitor.Enter(lockObject);
try
{
    // Code to execute...
}
finally
{
    Monitor.Exit(lockObject);
}
```

-   **Mutexes and Semaphores**: Mutex and Semaphore classes can be used to control access over a pool of resources.
-   **Thread-Safe Collections**: .NET provides inherently thread-safe collections like `ConcurrentBag`, `ConcurrentQueue`, `ConcurrentStack`, `ConcurrentDictionary`, etc.

## What are the differences between a struct and a class in C#?

### Answer

While structs and classes in C# are quite similar, there are many key differences between them:

-   **Value Type vs Reference Type**: The most significant difference is that struct is a value type and class is a reference type. When a struct is assigned to a new variable, a completely new copy of the value is created and changes made to one will not affect the other. However, with classes, assigning an instance of a class to a new variable doesn’t create a new object but creates a new reference to the existing object.
-   **Inheritance**: Structs in C# cannot inherit from a class or struct, but it can implement an interface. But classes in C# can inherit from a class or implement an interface.
-   **Default Constructor**: Structs do not contain a default (parameterless) constructor, and the compiler does not add one. Classes, however, have a default constructor if no constructors are specified.
-   **Nullability**: Structs cannot be null as they are a value type, whereas classes being a reference type can have a null value.

## What are Attribute classes and how are they beneficial?

### Answer

Attribute classes in C# are a way for adding declarative information to code. It is a kind of metadata that can be added to the assemblies, types, methods, fields etc. at compile time and can be queried at runtime using reflection.

Benefits of Attribute Classes:

-   **Code readability and maintainability**: With attribute classes, you can control behavior of methods or classes without affecting their source code.
-   **Metadata Access at Runtime**: You can access metadata at runtime for affecting the behavior or else for diagnostics / inspection purpose.
-   **Controlling behavior**: Custom attributes can be queried at runtime and accordingly certain behaviors can be enabled or disabled, without hard-coding them.

For example, Conditional attribute in C# is used for conditional compilation:

```csharp
class Program
{
    [Conditional("DEBUG")]  
    static void DebugMethod()
    {
        Console.WriteLine("Debug mode");
    }  
}
```

In this example, `DebugMethod` will only be called if “DEBUG” symbol is defined.

Well, there you have it! Those were the 20 mid-high level C# interview questions. Bet they got your brain cells working, huh? But remember, each challenge faced is another step up the ladder.

Stick around as we continue to navigate through the complex world of C#, with more mind-boggling challenges up our sleeves. Until then, keep learning, keep growing.