Top 62 C# Interview Questions [2023] | Ace the job interview

By Josip MiskovicUpdated on
A thumbnail showing  C# interview questions.
All C# Interview Questions:

C# Interview Questions

Let's get right into it. This is the best list of C# interview questions you'll ever find.

What's .NET?

.NET is a software framework developed by Microsoft. It is used for building and running applications across various platforms such as Windows, Linux, and macOS.

C# provides the syntax, rules, and structure for writing code, while .NET provides the runtime environment, tools, and libraries needed for those applications to run and interact with different platforms.

What's EF Core?

EF Core (Entity Framework Core) is an open-source Object-Relational Mapping (ORM) library developed by Microsoft. It lets us work with databases using .NET objects, instead of interacting with databases directly. EF Core simplifies data access and helps us manage database connections.

EF Core works with various databases, including:

  • SQL Server
  • SQLite
  • PostgreSQL
  • MySQL
  • Oracle
  • Azure Cosmos DB
  • InMemory (testing purposes)

It supports additional databases through third-party providers.

What is Nullable?

The Nullable value type in C# is a representation that allows non-nullable types to be null.

For example, the default value for int is 0. An int cannot be null. What do we do if we want to indicate that the value is missing? Zero could present a valid value.

We can wrap the int type with Nullable. With nullable types, you can set the variable to null. Explicitly using nullable types makes it clear that the variable doesn't have a value.

Behind the scenes, the Nullable<T> type is just a wrapper around the actual value. It's a struct that will contain null values unless otherwise specified.

What is a Struct?

A C# struct is a value type with the main purpose of storing data in a structured way. Classes are more about defining behavior, while structs give us a way to structure data.

Structs are stored on the stack and they cannot be null. Unlike classes, structs do not support inheritance. Also, you do not need to use the new keyword to create a struct.

So, structs:

  • Are value types
  • Do not support inheritance
  • Can implement interfaces
  • Are stack-allocated
  • Cannot be null

What's a reference type vs a value type?

The main difference between reference types and value types is that value types are meant to be small and lightweight, while reference types form more complex class hierarchies.

When we pass a value type to a method, the instance of that value type is copied. On the other hand, reference types pass a memory reference, so they are not copied.

Example: Reference Type vs Value Type

In C#, an integer (int) is an example of a value type, while a string is a reference type.

When we pass an integer to a method, a copy of that integer value is passed. And any changes made to that integer within the method do not affect the original integer outside the method.

What's the difference between a Class and a Struct?

The main difference between struct and class in C# is that structs are value types, while classes are reference types. Classes support inheritance, but structs don't. Classes can have a null reference, while structs can't.

This table summarizes the differences between structs and classes:

ClassStruct
Reference typeValue type
Support inheritanceNo inheritance
Can have null referenceCannot have null reference
Contain complex logicLight on logic, they structure data
Can have parameterless constructorsCan have parameterless constructors

What's the difference between async and sync?

The main difference between async and sync programming in C# is that async allows for non-blocking code execution, while, sync blocks the calling thread until the called method returns. With async, the thread continues to execute other code while the called method is running.

Sync is simpler but can hurt performance, while async can improve performance but is more complex because it requires knowledge of threading, parallelism, and mental models.

What's the difference between checked and unchecked?

The main difference between checked and unchecked is that the checked keyword enables overflow checking, while the unchecked keyword disables it. In a checked context, an exception is thrown if the result is out of range, while unchecked truncates the result to fit the range.

Keyword Description
checked

Enables overflow checking. An exception is thrown at runtime if an arithmetic operation or conversion results in a value that is outside the range of the destination type.

unchecked

Disables overflow checking. The result of an arithmetic operation or conversion is truncated to fit within the range of the destination type, and no exception is thrown.

What are Boxing and Unboxing?

In C#, boxing is converting a value type to an interface that this value type implements.

For example, converting a struct to an interface causes boxing.

When we box a value, it creates a new object on the heap and copies the value into it. The new allocation might lead to performance degradation.

C# Boxing of a struct

An example of boxing in the Intermediate Language (IL) is when we box a struct.

What is ?? Operator?

The C# double question mark operator ?? is known as the null coalescing operator.

In a nutshell, the null coalescing operator means: "if the first value is null, use this other value."

The null coalescing operator is a binary operator that takes two operands:

  1. an expression that can be null,
  2. the value that you want to assign to the expression if it is not null.

The null coalescing operator is a useful way to provide a default value for a variable.

What is a sealed class?

In C#, a sealed class is a class that cannot be inherited by other classes. Once a class is marked as sealed, it cannot be used as a base class for any other class. Any attempt to derive a new class from a sealed class will result in a compile-time error.

Sealing a class is a way of ensuring that the class cannot be modified in unintended ways by other developers. It also allows the developer to guarantee certain class-level invariants, such as immutability. Additionally, sealing a class can improve the performance of the application by reducing the overhead associated with virtual method calls.

What's the difference between using dynamic and object?

The main difference between Object type and dynamic type in C# is that Object type is statically typed, while dynamic variables are dynamically typed. Moreover, for Object, the C# compiler checks types during compile-time, but for dynamic, it checks types during runtime.

Object class is the root of all classes, so it's used everywhere. On the other hand, we use the dynamic type rarely. We mostly use it to make interaction with other dynamic languages easier.

Since Object type is strongly-typed, Visual Studio IntelliSense provides suggestions as we type, but for dynamic type, the IntelliSense doesn't exist.

What are optional parameters?

C# optional parameters are a way to specify the parameters that are required and those that are optional.

If we do not pass the optional argument value at calling time, it uses the default value.

Inside the method definition, we define a default value for the optional argument.

The compiler won't complain if we don't pass arguments for the optional parameters. The compiler knows to use the default value for any omitted parameters.

C# Optional Parameters.

What's a tuple?

C# tuple is a lightweight data structure that provides concise syntax to group multiple data elements.

A tuple has the following syntax:

(type1, type2,...)

For example, to represent a person's name and age we can use a tuple as follows:

(string firstName, int age)

C# tuple can hold any number of elements. But, the number of elements in a tuple must be fixed at compile time.

The easiest way to create a tuple is to use parentheses syntax.

You can also create a tuple by specifying the type name, followed by the element types in parentheses. For example:

C#
var latLong = (25.2, 12.2);

Console.WriteLine(latLong); // (25.2, 12.2)

What's the difference between varand specific types?

The main difference between VAR and type in C# is that the var keyword defines an implicitly typed local variable, while using a type explicitly sets the type of a variable. When you use var in place of the type declaration you let the compiler infer the type. But when you use explicit type, the variable declaration must be of the type you specified.

We use var to make our developer experience better, simplify, and increase code readability. On the other hand, explicit typing is closer to the strongly-typed nature of C#.

Var is a language feature that lets us use the var keyword in place of variable types. The compiler infers the variable type and replaces it with the proper type.

C# introduced the var keyword in C# 3.0 back in 2007. Since then, there has been a heated debate over if we should use VAR or explicit type.

What's the difference between IEnumerable and List?

The main difference between IEnumerable and List in C# is that IEnumerable is an interface, while List is a concrete class. Moreover, IEnumerable is read-only and List is not. List represents the entire collection in memory, while IEnumerable provides an interface for getting the next item one-by-one (enumerating data).

Both IEnumerable and List are part of .NET's System.Collections namespace.

What's the difference between IEnumerable and IQueryable?

The main difference between IEnumerable and IQueryable in C# is that IQueryable queries out-of-memory data stores, while IEnumerable queries in-memory data. Moreover, IQueryable is part of .NET's System.LINQ namespace, while IEnumerable is in System.Collections namespace.

IQueryable contains methods for constructing expression trees. IQueryable inherits IEnumerable, so IQueryable does everything that IEnumerable does. IQueryable extends the IEnumerable with logic for querying data.

What's the difference between: Public vs Sealed vs Abstract Classes?

Public, sealed, and abstract are access modifiers that achieve different things in C#:

  • Public classes are accessible from anywhere within the program and can be inherited by other classes.
  • Abstract" classes are intended to be used as a base class and cannot be instantiated. They are intended to be overridden by derived classes.
  • Sealed classes cannot be inherited and cannot have any derived class. This provides a stronger level of encapsulation and protection against unintended modifications. Additionally, sealed classes are more efficient in terms of performance as they don't have virtual method calls

The table gives you a clear understanding of the main differences between the 3 access modifiers:

Modifier Can Inherit Can Instantiate Can Override Performance
Public Yes Yes Yes Normal
Abstract Yes No Yes Normal
Sealed No Yes No Greater

What's the difference between virtual and sealed?

Virtual and sealed methods are two different ways to control the behavior of a class through inheritance in C#.

Virtual methods allow for flexibility in modifying the behavior of a class through inheritance, while sealed methods provide a stronger level of encapsulation and protection against unintended modifications. Virtual methods can be overridden by derived classes to provide a specialized implementation, while sealed methods cannot. In terms of performance, virtual method calls add overhead to the performance, as the runtime must determine the correct implementation of a method to call at runtime. On the other hand, sealed methods do not have virtual method calls, which results in a performance boost.

What is managed and unmanaged code?

Managed code refers to code that is executed by a runtime environment. For example, the .NET Core runtime. The .NET Core runtime manages and provides services such as memory management and security. The code is compiled into an intermediate language (IL) and then executed by the runtime environment. Managed code is portable and can run on different operating systems as long as the runtime environment is available.

On the other hand, unmanaged code refers to code that is executed directly by the operating system. Unmanaged code doesn't use a runtime environment. Unmanaged code is compiled directly into machine code and is dependent on the specific operating system and hardware architecture. Unmanaged code can be accessed from managed code, but it may limit the portability of the program to specific operating systems or hardware architectures.

What is a namespace?

In programming, a namespace is a way to organize and group related classes, interfaces, structures, and types. Namespaces help avoid naming conflicts by providing a unique name for each group of types. They also provide a way to create modular and reusable code, as they can be shared across different projects or libraries.

In the .NET Framework, each namespace is identified by a unique name. The namespace can consist of multiple parts separated by periods. For example:

  • the System.IO namespace contains types related to input and output operations,
  • the System.Collections.Generic namespace contains generic collection types.

What's a using statement?

We use namespaces in our code by importing them with a using statement or by specifying the fully qualified name of the type, which includes the namespace name.

This allows them to access the types defined in the namespace without having to fully specify the namespace each time.

Is C# statically or dynamically-typed language?

C# is a type-safe language that supports static typing. Static typing ensures type safety during compile time and during runtime.

Type safety is a fundamental concept in C# programming. It means that each type is internally consistent and can only interact with other types through their defined protocols.

Static typing eliminates a significant class of errors before a program is executed.

The fact that C# uses static typing enables IntelliSense in Visual Studio. The complier knows the variable's type and the methods that can be called on it.

C# is also known as a strongly typed language because its type rules are strictly enforced. For example, calling a function that accepts an integer with a floating-point number is not allowed unless the floating-point number is explicitly converted to an integer.

How does C# handle integer overflow?

C# handles integer overflow by defaulting to "wraparound" behavior. Instead of throwing an exception, the extra significant bits are discarded.

This code doesn't throw an exception because the value is increased during runtime:

C#
int myInt = int.MaxValue
Console.WriteLine (myInt+1 == int.MinValue); // True

This code throws an exception because the value is increased statically:

C#
Console.WriteLine (int.MaxValue+1 == int.MinValue); //  The operation overflows at compile time in checked mode.

What's the difference between the Stack and the Heap in C#?

The stack is used for storing local variables and parameters, while the heap is used for storing objects.

When you call a method or function, the local variables and parameters are stored on the stack. As soon as the method or function exits, the memory for those variables is deallocated. Thus, the stack grows and shrinks dynamically based on the current function calls.

On the other hand, the heap is used for storing objects. Whenever you create a new object, it is allocated on the heap, and a reference to that object is returned. The garbage collector periodically deallocates objects from the heap to ensure that your program doesn't run out of memory. An object becomes eligible for deallocation as soon as it's not referenced by anything.

If the instance was declared as a field within a class type or as an array element, that instance lives on the heap.

What's the difference between ref and out?

The ref parameter modifier lets us pass arguments to methods by reference. The out parameter modifier lets us return arguments from methods by reference.

The ref keyword is used when you want to pass a variable as an input to the method. It lets modify the original value of that variable. On the other hand. the out keyword is used when you want to pass back a variable as an output to the method. A variable passed using out cannot be assigned before going into the function, but it must be assigned before it comes out.

Is there a way to just import a type rather than the entire namespace?

Yes, the using static directive imports a specific type.

C#
using static System.Collections.Generic.List;

What is an abstract Class?

An abstract class is a base class that cannot be instantiated. It contains abstract methods, which derived classes must implement. It's used for providing common functionality and enforcing a contract for subclasses.

What's an Interface?

An interface is a contract that contains method signatures. It doesn't contain implementation, instead it acts as a blueprint.

An interface is used to define the behavior that a class must implement. It is used when you want to enforce a specific structure or behavior on classes that implement the interface.

We use interfaces for abstraction, multiple inheritance, and loose coupling.

What is the difference between an abstract class and an interface?

The main difference between an abstract class and an interface is that an abstract class is a base class with partial implementation, while an interface defines a contract without implementation. Abstract classes accommodate shared implementation, while interfaces support independent implementations. Abstract classes permit fields, constructors, and concrete methods; interfaces do not. Classes can inherit one abstract class and multiple interfaces.

What's an Enum?

An enum (enumeration) is a data type that consists of a set of named constants. We use enums to give names to a group of related values.

By using enums, we restrict variable to only have specific values or states. For example: days of the week, directions (left, right), or colors.

We use enums to improve code readability and reduce errors.

What's in an Assembly?

An assembly is a container of code that consists of compiled code unit. It also contains metadata which describes types in the compiled code. Assemblies are created during the compilation process and used during application runtime.

We use assemblies for modular programming, code reusability, and versioning. For example, when referencing libraries or sharing code between projects.

What is a Type?

A type is a strictly defined structure of data and behavior.

Types are important because they ensure data consistency, provide validation of input, and enable compile-time error checking.

Types are used:

  1. When declaring variables or objects, to specify the kind of data the variable can store or the object represents.
  2. When creating classes or structures, to define their structure and behavior.
  3. When defining function parameters, to specify the kind of data the function accepts as input.
  4. When defining function return values, to specify the kind of data the function returns.

What's the difference between an argument and a parameter?

A parameter is a variable declared in a method definition which specifies what inputs the method expects. An argument is the actual value passed to the method when it is called. Arguments correspond to the defined parameters.

What is the Common Language Runtime or CLR?

Common Language Runtime (CLR) is service that provides a runtime environment for executing managed code. The environment handles memory allocation, garbage collection, and provides important services such as security and exception management.

C# is called a managed language because it relies on the CLR to manage memory, resources, and execution. It compiles source code into Intermediate Language (IL), which is platform-agnostic. During runtime, the CLR uses Just-In-Time (JIT) compilation to convert IL into native machine code.

What is Lazy<T>?

Lazy<T> is a class in C# that provides on-demand object initialization. That means the object is only created when it's first accessed. It is useful for optimizing performance when creating expensive objects that may not be needed during the execution of a program.

Lazy<T> can be initialized in both thread-safe and thread-unsafe fashion.

What is a Class?

A class is the most common reference type in C#. It's a blueprint for creating objects.

We use a class to define properties and methods for a specific type of object. To use it, declare a class with the "class" keyword, followed by the class name and a pair of curly braces containing its members. Use a class when you need to represent an entity with data and behavior.

What is a Record?

A Record is a new C# data structure introduced in C# 9.0. It provides a simple, efficient way to define immutable types for storing data. Records are particularly useful for grouping data and handling it as a single unit, while automatically generating useful methods like equality comparison and copying.

What's the difference between a class and a record?

The main difference between class and record type in C# is that a record has the main purpose of storing data, while a class defines responsibility. Records are immutable, while classes are not.

Simply put, a class is an OOP concept that wraps data with functionality, while a record represents a set of data.

Other differences between class and record type include:

  • We define records using the record keyword instead of the class keyword.
  • Records should not have any state changes after instantiation, while classes change properties.
  • We create new records from existing ones when we want to change state. With classes, we modify the existing ones.

What are generics?

Generics are a feature in C# that allows you to define classes, interfaces, or methods with placeholders for the data types.

Generics enabling type-safe code reusability and reducing code duplication.

Imagine you're building a zoo management system. You need to create enclosures for different animal types like lions, zebras, and elephants. Without generics, you would create separate classes for each enclosure type.

C#
public class LionEnclosure
{
    public List<Lion> Lions;
}

public class ZebraEnclosure
{
    public List<Zebra> Zebras;
}

public class ElephantEnclosure
{
    public List<Elephant> Elephants;
}

With generics, you can create a single, reusable class for any animal type:

C#
public class Enclosure<T>
{
    public List<T> Animals;
}

Now, you can create instances for different animal types:

C#
var lionEnclosure = new Enclosure<Lion>();
var zebraEnclosure = new Enclosure<Zebra>();
var elephantEnclosure = new Enclosure<Elephant>();

What are delegates?

Delegates are a type of variable that holds a reference to a method in C#.

A delegate type specifies the method signature, which includes the return type and parameter types. Delegate instances can only call methods that match this signature. This ensures type safety and consistency when invoking methods through delegates.

With delegates, we can:

  • Pass methods as arguments to other methods.
  • Define and implement event handling.
  • Invoke methods with the same signature from multiple classes.
  • Create callback mechanisms for async operations.

What's a lambda expression?

A lambda expression is a concise way to represent an anonymous function. It is commonly used for passing small functions as arguments or for creating simple, single-use delegates or expression trees.

The lambda expression syntax consists of parameters, a lambda operator (=>) and an expression or statement block.

Example:

C#
Func<int, int, int> add = (x, y) => x + y;
int sum = add(3, 5); // Output: 8

What's an Anonymous Method?

An anonymous method is an unnamed, inline block of code that can be used as a delegate.

C#
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// Filter even numbers with anonymous method
IEnumerable<int> evenNumbers = numbers.Where(delegate (int x)
{
    return x % 2 == 0;
});

Exceptions

What's try/catch?

The try and catch keywords that define blocks of code that handles exceptions.

If the code inside the "Try" block throws an exception, the program doesn't crash. Instead, the "Catch" block gets executed.

How to throw an exception?

To throw an exception in C#, use the throw keyword followed by a new instance of the exception class. For example:

throw new Exception("Your error message here");

How to rethrow an exception?

To rethrow an exception in C#, use the throw keyword without specifying an exception object in a catch block. Example:

C#
try
{
    // Your code here
}
catch (Exception ex)
{
    // Log or process the exception
    throw; // Rethrow the caught exception
}

What are exception filters?

n C#, an exception filter is a feature that lets us handle exceptions based on a condition. Exception filters are used with the combination of catch and when keywords. In the when block, we specify a boolean expression that determines whether the exception should be caught. If the expression evaluates to true, the exception is caught and the code in the catch block is executed. Otherwise, the exception is passed to the next catch block.

How to create a custom exception?

In C#, you can create custom exceptions by creating a new class that derives from the Exception class. The custom exception class can also have additional properties or methods that you need.

Here is an example of how you could create a custom exception class in C#:

C#
public class InvalidInputException : Exception
{
    public InvalidInputException() : base() { }
    public InvalidInputException(string message) : base(message) { }
    public InvalidInputException(string message, Exception innerException) : base(message, innerException) { }
}

What's a Stream?

A Stream is an abstraction that represents a sequence of bytes used for reading from or writing to storage mediums. For example, streaming to files, memory, or network sockets.

Streams process data in chunks, reducing memory consumption and improving performance.

What's an Attribute?

An Attribute is a declarative metadata tag that describes classes, properties, or methods.

An Attribute inherits from the System.Attribute abstract class. It defines an attribute that can be attached to a code element. For example:

C#
[Obsolete] // Same as [ObsoleteAttribute]
public class MyOldClass { /*...*/ }

In this example, ObsoleteAttribute is attached to MyOldClass. The compiler recognizes the attribute and generates warnings if the obsolete element is referenced.

What is the purpose of the yield keyword in C#?

The yield keyword in C# is used in iterator methods to simplify creating custom enumerators.

It allows returning elements one at a time in a collection, without the need to create a separate collection or manage the enumerator's state explicitly.

Yielding is helpful when working with large sequences because it enables better memory usage.

What is LINQ?

LINQ (Language Integrated Query) is a feature in .NET that allows querying data from various data sources, such as collections or databases.

LINQ offers query capabilities for multiple data types, including arrays, lists, XML, and databases. It integrates directly with C#, using methods or query expressions, making it powerful and flexible. LINQ provides standard methods like filtering, sorting, joining, and grouping, reducing the need for custom-made code. It improves productivity and code quality by abstracting complex data operations.

LINQ offers the benefits of both compile-time type checking and dynamic query composition.

How to get unique items from a list using LINQ?

To get unique items from a list using LINQ, you can use the Distinct() method.

You can also use DistinctBy() method if you have a collection of objects. The DistinctBy() lets you get unique elements from a list based on a property.

What's Disposable?

Disposable is a design pattern in C# that allows releasing of expensive unmanaged resources when an object is no longer needed. For example, file handles, network connections or memory. It is implemented using the IDisposable interface, which contains a Dispose() method for releasing resources.

What is the purpose of the using statement in C#?

The using statement in C# is used to ensure that resources defined in a code block are disposed after their usage.

The using statement is a convenient way to utilize Dispose(). It creates a scope where the resource is used and automatically calls Dispose() when the scope ends, ensuring that resources are always released, even if an exception occurs, without having to manually call Dispose().

How to know if object can be disposed of?

An object can be disposed of if it implements the IDisposable interface. You can check this by looking at the object's class definition or using the is keyword in C# to see if the object implements IDisposable.

What is a static class?

A static class is a class that cannot be instantiated or inherited. It contains only static members and methods, making it accessible directly without creating an object. Static classes are useful for grouping methods that don't depend on object instances.

A common example of a static class is the Math class in C#. It provides mathematical functions such as Pow, Sqrt, and Sin. You can use these methods directly without creating an instance:

C#
double result = Math.Sqrt(16); // result is 4

Here, Math is the static class and Sqrt is the static method.

What are the differences between static and instance methods?

  • Static methods belong to a static class or the entire class itself, while instance methods belong to a specific object or instance of the class.
  • Static methods can be called directly using the class name, whereas instance methods need an object to be called.
  • Static methods can access only static members, while instance methods can access both static and instance members.
  • Static methods cannot use the this keyword, while instance methods can use this to reference the current object.

What is Reflection?

Reflection is a C# feature that let us interact with metadata of assemblies, types, and objects at runtime.

It enables dynamically loading assemblies, viewing type information, creating instances, and invoking methods or accessing properties.

What's a Thread?

A Thread is a separate execution path in a program. Threads allow multiple tasks to run concurrently or simultaneously within a single application.

Threads improve performance by utilizing CPU resources efficiently. They divide work among multiple tasks, enabling responsive user interfaces.

What is the difference between method overriding and method overloading in C#?

Method overriding is a programming practice where a derived class provides a new implementation for a method that's already defined in its base class.

Method overloading is a programming practice of defining multiple methods with the same name but different parameters in the same class. It allows for flexibility in method calls based on parameter types and counts.

What's the purpose of the as keyword?

The as keyword in C# is used for safe type conversion. It attempts to cast an object to a specific type without raising an exception. If the cast fails, it returns null instead.

What's the difference between numeric conversion, reference conversions, and boxing conversions?

Numeric conversion is changing a value from one numeric data type to another. Like int to float or long to double.

Reference conversion is converting a reference of one type to another. Such as casting derived class objects to their base class or interface.

Boxing conversion happens when a value type (like int, float) is converted to an object or interface type. The process involves wrapping the value type into an object.

Josip Miskovic
About Josip

Josip Miskovic is a software developer at Americaneagle.com. Josip has 10+ years in experience in developing web applications, mobile apps, and games.

Read more posts →

Last modified on:
Download Free Software Developer Career Guide

I've used these principles to increase my earnings by 63% in two years. So can you.

Dive into my 7 actionable steps to elevate your career.