DEV Community

Cover image for 20 C# Interview Questions (for Experienced) 2023
ByteHide
ByteHide

Posted on • Originally published at bytehide.com

20 C# Interview Questions (for Experienced) 2023

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:

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

// File2.cs
public partial class MyClass
{
    public void Method2() { /* implementation */ }
}
Enter fullscreen mode Exit fullscreen mode

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.

Example:

int value = 42;
object boxed = value; // Boxing
Enter fullscreen mode Exit fullscreen mode

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:

object boxed = 42;
int value = (int)boxed; // Unboxing
Enter fullscreen mode Exit fullscreen mode

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.
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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. 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.
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
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Using the async and await keywords for asynchronous programming. This approach simplifies working with long-running methods without blocking the calling thread.
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
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

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

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:

DisplayMessage display = message => Console.WriteLine(message);
Enter fullscreen mode Exit fullscreen mode

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

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

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:

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

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:

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

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:

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

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

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.

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

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:

ThreadPool.QueueUserWorkItem(new WaitCallback(SomeMethod));
Enter fullscreen mode Exit fullscreen mode

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:

int? number = null;
Enter fullscreen mode Exit fullscreen mode

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:

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

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:

Func<int, int> square = x => x * x;
Enter fullscreen mode Exit fullscreen mode

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.
private static object _lock = new object();

public void UpdateCounter()
{
    lock(_lock)
    {
        counter++;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Monitor: Monitor class provides exclusive lock similar to lock keyword.
Monitor.Enter(lockObject);
try
{
    // Code to execute...
}
finally
{
    Monitor.Exit(lockObject);
}
Enter fullscreen mode Exit fullscreen mode
  • 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:

class Program
{
    [Conditional("DEBUG")]  
    static void DebugMethod()
    {
        Console.WriteLine("Debug mode");
    }  
}
Enter fullscreen mode Exit fullscreen 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.

Top comments (3)

Collapse
 
mikebarker profile image
Mike Barker

With respect to the question regarding IEnumerable, ICollection, and IList.. this distinction is only true for the generic types (ICollection, and IList). For the non-generic interfaces the distinction is less obvious, but ICollection is a non-virtual collection which has size and an inherent nature of "storage structure". IList has the ability to modify the collection, AND has inherent order (ie access by index).

Collapse
 
schemetastic profile image
Schemetastic (Rodrigo) • Edited

Wow! Really interesting!

Collapse
 
indika_wimalasuriya profile image
Indika_Wimalasuriya

Great write-up! Clear, detailed, and valuable insights. Excellent work