C# is a object-oriented programming languages used for developing desktop, web, cloud, mobile and enterprise applications on the .NET platform. Due to its extensive use in software development, it is a common topic in technical interviews for both freshers and experienced professionals. Understanding C# fundamentals, object-oriented programming, collections, multithreading, LINQ and .NET concepts can help candidates confidently answer a wide range of interview questions.
1. What is C#?
C# (pronounced C-Sharp) is a modern, object-oriented programming language developed by Microsoft. It runs on the .NET platform and is used to build a wide variety of applications, including desktop software, web applications, mobile apps, cloud services, games, and APIs.

2. What is .NET Framework (or .NET) and how does it work?
.NET is a software development platform developed by Microsoft for building and running applications. It provides libraries, runtime services, and tools that simplify application development. When a C# program is compiled, it is first converted into Intermediate Language (IL). The Common Language Runtime (CLR) then converts the IL into machine code during execution so it can run on the operating system.
3. What is the Common Language Runtime (CLR)?
Common Language Runtime (CLR) is the execution engine of the .NET platform. It is responsible for running .NET applications and managing various runtime services automatically. CLR performs tasks such as:
- Converting IL code into machine code using Just-In-Time (JIT) compilation.
- Managing memory through Garbage Collection (GC).blueprint or template that defines the properties (data) and methods (behavior) of an object. An object is an actual instance of a class that can store data and perform the actions defined by that class.
- Handling exceptions.
- Providing security and type safety.
- Managing thread execution.
Without the CLR, a C# application cannot execute.
4. What is Intermediate Language (IL/MSIL)?
Intermediate Language (IL), also known as Microsoft Intermediate Language (MSIL), is the CPU-independent code generated when a C# program is compiled. Instead of producing machine code directly, the C# compiler generates IL code. During execution, the CLR converts this IL into native machine code using the Just-In-Time (JIT) compiler.
Compilation process:
C# Source Code
↓
C# Compiler
↓
Intermediate Language (IL)
↓
CLR + JIT Compiler
↓
Machine Code
5. What is the difference between Managed and Unmanaged Code?
Managed code is executed under the control of the CLR, whereas unmanaged code runs directly on the operating system without CLR management.
Managed Code:
- Executes under the CLR.
- Memory is managed automatically using Garbage Collection.
- Provides better security and exception handling.
- Used by C# and other .NET languages.
Unmanaged Code:
- Executes directly on the operating system.
- Memory must be managed manually.
- Usually written in languages such as C or C++.
- Can offer greater control over hardware and memory.
In most C# applications, developers primarily work with managed code.
6. What are Value Types and Reference Types?
C# stores data using either value types or reference types, depending on how the data is represented in memory.
Value Types:
- Store the actual value directly.
- Each variable has its own copy of the data.
- Changes made to one variable do not affect another.
- Examples include int, float, bool, char, and struct.
Reference Types:
- Store a reference (address) to the actual object.
- Multiple variables can refer to the same object.
- Changes made through one reference are visible through other references.
- Examples include class, string, array, and interface.
Example: The example below demonstrates a value type. Since int is a value type, assigning one variable to another creates a separate copy of the value.
int a = 10;
int b = a;
b = 20;
Console.WriteLine(a);
Console.WriteLine(b);
Output
10
20
7. What is the difference between var, dynamic and explicit type declaration?
All three can be used to declare variables, but they differ in how the type is determined and checked.
Explicit Type Declaration
- The data type is specified manually.
- Type checking happens during compilation.
- Makes the code easier to understand.
int age = 25;
var
- The compiler automatically determines the variable's type during compilation.
- The type cannot change later.
- Still provides compile-time type checking.
var name = "Emma";
Here, name is treated as a string.
dynamic
- The type is determined at runtime.
- Compile-time type checking is skipped.
- Useful when the object's type is not known until execution.
dynamic value = 10;
value = "Hello";
value = true;
Use explicit type declaration when the data type is known and readability is important, use var when the compiler can clearly infer the type and use dynamic only when the type is unknown until runtime, such as when working with COM objects, reflection, or dynamic data.
8. What is Boxing and Unboxing?
Boxing is the process of converting a value type into a reference type (object), while Unboxing is the process of converting the boxed object back into its original value type.


Example: The example below demonstrates both boxing and unboxing.
int number = 100;
// Boxing
object obj = number;
// Unboxing
int value = (int)obj;
Console.WriteLine(obj);
Console.WriteLine(value);
Output
100
100
Explanation:
- During boxing, the value of number is copied into an object, which is stored on the managed heap.
- During unboxing, the object is explicitly cast back to its original value type (int).
- Boxing happens automatically, whereas unboxing always requires explicit type casting.
- Frequent boxing and unboxing can impact performance due to additional memory allocation and type conversion.
9. What is the difference between const and readonly?
Both const and readonly are used to create values that should not be modified, but they differ in when their values are assigned.
const
- The value must be assigned at the time of declaration.
- The value is fixed at compile time.
- It is implicitly static and shared across all instances.
- Suitable for values that never change, such as mathematical constants.
const double PI = 3.14159;
Console.WriteLine(PI);
Output
3.14159
readonly
- The value can be assigned either at declaration or inside the constructor.
- The value is determined at runtime.
- Each object can have its own readonly value.
- Suitable for values that are initialized only once when an object is created.
using System;
class Student
{
public readonly int Id;
public Student(int id)
{
Id = id;
}
}
Student student = new Student(101);
Console.WriteLine(student.Id);
Output
101
Use const for compile-time constants whose values never change and use readonly when the value is known only during object creation or at runtime and should remain unchanged afterward.
10. What are the different parameter types in C# (ref, out, in and value parameters)?
C# supports different ways of passing arguments to methods depending on whether the method needs to read or modify the value.
Value Parameter
- Passed by value (a copy is created).
- Changes inside the method do not affect the original variable.
using System;
void Display(int x)
{
x = 20;
}
int a = 10;
Display(a);
Console.WriteLine(a);
Output
10
ref Parameter
- Passed by reference.
- The variable must be initialized before passing.
- Changes inside the method affect the original variable.
using System;
void Update(ref int x)
{
x = 20;
}
int a = 10;
Update(ref a);
Console.WriteLine(a);
Output
20
out Parameter
- Passed by reference.
- The variable does not need to be initialized before passing.
- The called method must assign a value before returning.
using System;
void GetValue(out int x)
{
x = 50;
}
int number;
GetValue(out number);
Console.WriteLine(number);
Output
50
in Parameter
- Passed by reference but is read-only.
- The method cannot modify its value.
- Useful for passing large objects efficiently without copying them.
using System;
void Display(in int x)
{
Console.WriteLine(x);
}
int number = 100;
Display(number);
Output
100
11. What is method overloading?
Method overloading allows multiple methods to have the same name but different parameter lists within the same class. Methods can be overloaded based on:
- Number of parameters.
- Type of parameters.
- Order of parameters.
Example: The example below overloads the Add() method to work with both integer and double values.
using System;
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
}
Calculator calc = new Calculator();
Console.WriteLine(calc.Add(10, 20));
Console.WriteLine(calc.Add(10.5, 20.5));
Output
30
31
Explanation:
- Both methods have the same name (Add) but accept different parameter types.
- The compiler selects the appropriate method based on the arguments passed.
- Method overloading is an example of compile-time polymorphism in C#.
12. What is recursion and when should it be used?
Recursion is a programming technique in which a method calls itself until a base case (stopping condition) is reached. It is commonly used to solve problems that can be broken down into smaller, similar subproblems.
Example: The example below uses recursion to calculate the factorial of a number.
using System;
int Factorial(int n)
{
if (n <= 1)
return 1;
return n * Factorial(n - 1);
}
Console.WriteLine(Factorial(5));
Output
120
Common uses of recursion include:
- Calculating factorials.
- Generating the Fibonacci series.
- Traversing trees and graphs.
- Searching hierarchical data structures.
- Solving divide-and-conquer problems such as merge sort and quicksort.
13. What is the difference between for and foreach loops?
Both loops are used to iterate over collections, but they serve different purposes.
for Loop
- Uses an index to access elements.
- Allows moving forward or backward.
- Suitable when the element index is required.
- Allows modifying array elements.
foreach Loop
- Iterates directly over each element.
- Does not require an index.
- Simpler and easier to read.
- Generally used for read-only iteration.
Example: The example below uses a foreach loop to print all elements of an array.
using System;
int[] numbers = { 10, 20, 30 };
foreach (int item in numbers)
{
Console.WriteLine(item);
}
Output
10
20
30
In general use for loop when you need the index or want to modify elements and use foreach loop when you simply want to read every element.
14. What is the difference between break, continue and return?
The break, continue and return statements are used to control the flow of program execution, but each serves a different purpose.
break
- Immediately terminates the current loop or switch statement.
- Execution continues with the first statement after the loop.
for (int i = 1; i <= 5; i++)
{
if (i == 3)
break;
Console.WriteLine(i);
}
Output
1
2
continue
- Skips the current iteration of the loop.
- Moves directly to the next iteration.
for (int i = 1; i <= 5; i++)
{
if (i == 3)
continue;
Console.WriteLine(i);
}
Output
1
2
4
5
return
- Exits the current method immediately.
- Can optionally return a value to the caller.
using System;
int Square(int n)
{
return n * n;
}
Console.WriteLine(Square(5));
Output
25
Use break to stop a loop or switch statement, use continue to skip the current iteration and continue with the next one and use return to exit a method, optionally returning a value.
15. What is the difference between Arrays and Lists?
Both Arrays and Lists are used to store collections of elements, but they differ in size, flexibility and functionality.
Array
- Has a fixed size after creation.
- Stores elements of the same data type.
- Provides faster access with lower memory overhead.
- Best suited when the number of elements is known in advance.
using System;
int[] numbers = { 10, 20, 30 };
Console.WriteLine(numbers[1]);
Output
20
List
- Size can grow or shrink dynamically.
- Supports many built-in methods such as Add(), Remove(), Insert() and Sort().
- More flexible than arrays.
- Suitable when the number of elements may change during execution.
using System;
using System.Collections.Generic;
List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);
Console.WriteLine(numbers.Count);
Output
3
In general, use Arrays when the collection size is fixed and performance is important and use Lists when elements need to be added, removed or modified dynamically.
16. What is the difference between string and StringBuilder?
Both string and StringBuilder are used to work with text in C#, but they differ in how they handle modifications.
string
- Immutable, meaning its value cannot be changed after creation.
- Every modification creates a new string object in memory.
- Suitable for small or infrequent string operations.
using System;
string message = "Hello";
message += " World";
Console.WriteLine(message);
Output
Hello World
StringBuilder
- Mutable, meaning its contents can be modified without creating a new object.
- More memory-efficient when performing multiple string operations.
- Suitable for building or modifying large strings repeatedly.
using System;
using System.Text;
StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.Append(" World");
Console.WriteLine(sb.ToString());
Output
Hello World
In general, use string for simple text operations where only a few modifications are needed and use StringBuilder when performing frequent string modifications to improve performance and reduce memory usage.
17. What is a class and what is an object?
A class is a blueprint or template that defines the properties (data) and methods (behavior) of an object. An object is an actual instance of a class that can store data and perform the actions defined by that class.
Example: The example below creates a Student class and then creates an object of that class.
using System;
class Student
{
public string Name;
public void Display()
{
Console.WriteLine(Name);
}
}
Student s1 = new Student();
s1.Name = "Emma";
s1.Display();
Output
Emma
Explanation:
- Student is a class that defines a Name field and a Display() method.
- s1 is an object (instance) of the Student class.
- The object's Name property is assigned the value "Emma", and Display() prints it to the console.
18. What are constructors and why are they used?
A constructor is a special method that is automatically called when an object is created. It is mainly used to initialize object data and ensure that the object starts with valid values.
- Has the same name as the class.
- Does not have a return type.
- Executes automatically when an object is instantiated.
Example: The example below uses a constructor to initialize the Name field.
using System;
class Student
{
public string Name;
public Student()
{
Name = "Jake";
}
}
Student s = new Student();
Console.WriteLine(s.Name);
Output
Jake
Explanation:
- Student() is the constructor of the Student class.
- It automatically assigns "Jake" to the Name field whenever a new Student object is created.
- Constructors help initialize objects with default or required values before they are used.
19. What is constructor overloading?
Constructor overloading means creating multiple constructors in the same class with different parameter lists. This allows objects to be initialized in different ways depending on the information available.
Example: The example below defines two constructors—one with no parameters and another that accepts a name.
using System;
class Student
{
public string Name;
public Student()
{
Name = "Unknown";
}
public Student(string name)
{
Name = name;
}
}
Student s1 = new Student();
Student s2 = new Student("Jake");
Console.WriteLine(s1.Name);
Console.WriteLine(s2.Name);
Output
Unknown
Jake
Explanation:
- The first constructor assigns a default value of "Unknown".
- The second constructor initializes the object using the value passed by the user.
- Constructor overloading provides multiple ways to create and initialize objects.
20. What are properties in C#?
Properties provide a controlled way to read and modify class fields using the get and set accessors. They help implement encapsulation by controlling how class data is accessed.
Example: The example below uses an auto-implemented property.
using System;
class Student
{
public string Name { get; set; }
}
Student s = new Student();
s.Name = "Robin";
Console.WriteLine(s.Name);
Output
Robin
Explanation:
- get retrieves the property's value and set assigns or updates the property's value.
- Auto-implemented properties ({ get; set; }) automatically create a private backing field, making the code shorter and cleaner.
- Properties are preferred over public fields because they provide better control over class data.
21. What are access modifiers in C#?
Access modifiers define the visibility and accessibility of classes, methods, properties, and variables. They help implement encapsulation by controlling where program members can be accessed. Common access modifiers are:
- public: Accessible from anywhere in the application.
- private: Accessible only within the same class.
- protected: Accessible within the same class and its derived classes.
- internal: Accessible only within the same assembly (project).
- protected internal: Accessible from the same assembly or from derived classes in another assembly.
- private protected: Accessible only within the same assembly and through inheritance.
Example: The example below uses both public and private access modifiers.
using System;
class Student
{
public string Name = "Emma";
private int Marks = 95;
public void DisplayMarks()
{
Console.WriteLine(Marks);
}
}
Student s = new Student();
Console.WriteLine(s.Name);
s.DisplayMarks();
Output
Emma
95
Explanation:
- Name is declared as public, so it can be accessed from outside the Student class.
- Marks is declared as private, so it cannot be accessed directly outside the class.
- The DisplayMarks() method is used to safely access and display the private field.
22. Explain the four pillars of Object-Oriented Programming (OOP).
Object-Oriented Programming (OOP) is based on four fundamental principles that make code modular, reusable, and easier to maintain.
1. Encapsulation: combines data and methods into a single unit (class) while restricting direct access to internal data. It is commonly implemented using private fields and public properties or methods.
using System;
class Student
{
private int marks;
public int Marks
{
get { return marks; }
set { marks = value; }
}
}
Student s = new Student();
s.Marks = 90;
Console.WriteLine(s.Marks);
Output
90
Explanation: marks field is private and cannot be accessed directly. The Marks property provides controlled access to it.
2. Inheritance: allows one class to acquire the properties and methods of another class, promoting code reuse and reducing duplication.
using System;
class Animal
{
public void Eat()
{
Console.WriteLine("Eating...");
}
}
class Dog : Animal
{
}
Dog dog = new Dog();
dog.Eat();
Output
Eating...
Explanation: Dog class inherits the Eat() method from the Animal class.
3. Polymorphism: allows the same method name to perform different tasks depending on the object or parameters. It is achieved through method overloading and method overriding.
using System;
class Animal
{
public virtual void Sound()
{
Console.WriteLine("Animal makes a sound");
}
}
class Dog : Animal
{
public override void Sound()
{
Console.WriteLine("Dog barks");
}
}
Animal obj = new Dog();
obj.Sound();
Output
Dog barks
Explanation: hides implementation details and exposes only the essential functionality to the user. It is commonly implemented using abstract classes or interfaces.
4. Abstraction: hides implementation details and exposes only the essential functionality to the user. It is commonly implemented using abstract classes or interfaces.
using System;
abstract class Animal
{
public abstract void Sound();
}
class Dog : Animal
{
public override void Sound()
{
Console.WriteLine("Dog barks");
}
}
Animal animal = new Dog();
animal.Sound();
Output
Dog barks
Explanation: abstract class defines what the object should do, while the derived class provides the actual implementation.
23. What is the difference between an Abstract Class and an Interface?
Both abstract classes and interfaces are used to achieve abstraction, but they serve different purposes.
Abstract Class:
- Can contain both abstract and non-abstract (implemented) methods.
- Can have constructors, fields, and properties.
- Supports code reuse through shared implementations.
- A class can inherit from only one abstract class.
Interface:
- Defines a contract that implementing classes must follow.
- Cannot store instance fields.
- Supports multiple interface implementation.
- Best suited for defining common behavior across unrelated classes.
In general, use an abstract class when classes share common implementation and use an interface when different classes need to provide the same functionality.
24. When should you use an Interface instead of an Abstract Class?
An interface should be used when different classes need to implement the same behavior but do not share a common base class. It defines a contract that implementing classes must follow without enforcing a specific implementation.
Use an Interface when:
- Multiple unrelated classes need the same functionality.
- Multiple inheritance of behavior is required (a class can implement multiple interfaces).
- You want to define a contract without providing implementation.
- Different classes may implement the same functionality in different ways.
Example: The example below shows two unrelated classes implementing the same interface.
using System;
interface IPrintable
{
void Print();
}
class Invoice : IPrintable
{
public void Print()
{
Console.WriteLine("Printing Invoice");
}
}
class Report : IPrintable
{
public void Print()
{
Console.WriteLine("Printing Report");
}
}
IPrintable invoice = new Invoice();
IPrintable report = new Report();
invoice.Print();
report.Print();
Output
Printing Invoice
Printing Report
Explanation:
- IPrintable defines a common contract with the Print() method.
- Both Invoice and Report implement the interface in their own way.
25. What is the difference between Method Overloading and Method Overriding?
Both method overloading and method overriding support polymorphism, but they differ in how and when they are used.
Method Overloading
- Multiple methods have the same name but different parameter lists.
- Occurs within the same class.
- Does not require inheritance.
- Resolved at compile time (Compile-time Polymorphism).
using System;
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
}
Calculator calc = new Calculator();
Console.WriteLine(calc.Add(5, 3));
Console.WriteLine(calc.Add(5.5, 3.2));
Output
8
8.7
Method Overriding
- A derived class provides a new implementation for a method in the base class.
- Requires inheritance.
- Uses the virtual and override keywords.
- Resolved at runtime (Runtime Polymorphism).
using System;
class Animal
{
public virtual void Sound()
{
Console.WriteLine("Animal Sound");
}
}
class Dog : Animal
{
public override void Sound()
{
Console.WriteLine("Bark");
}
}
Animal animal = new Dog();
animal.Sound();
Output
Bark
Use method overloading to create multiple versions of a method with different parameters and use method overriding to change the behavior of an inherited method.
26. What is the this keyword?
The this keyword refers to the current instance of a class. It is commonly used to access instance members and resolve naming conflicts between class fields and method parameters.
Example: The example below uses this to distinguish the class field from the constructor parameter.
using System;
class Student
{
private string name;
public Student(string name)
{
this.name = name;
}
public void Display()
{
Console.WriteLine(this.name);
}
}
Student s = new Student("Emma");
s.Display();
Output
Emma
Common uses of this:
- Refer to the current object.
- Differentiate class fields from parameters with the same name.
- Call another constructor in the same class using this().
- Pass the current object as an argument to another method.
27. What is the base keyword?
The base keyword refers to members of the parent (base) class. It is mainly used to access base class methods, properties, or constructors from a derived class.
Example: The example below calls the base class method before executing the derived class method.
using System;
class Animal
{
public virtual void Sound()
{
Console.WriteLine("Animal Sound");
}
}
class Dog : Animal
{
public override void Sound()
{
base.Sound();
Console.WriteLine("Bark");
}
}
Dog dog = new Dog();
dog.Sound();
Output
Animal Sound
Bark
Common uses of base:
- Call a base class constructor using base().
- Access an overridden base class method.
- Access hidden members of the base class.
28. What is a static class, and when should it be used?
A static class is a class that cannot be instantiated. It contains only static members and is used to group utility or helper methods.
- Cannot create objects.
- Contains only static members.
- Cannot inherit from other classes.
- Is automatically sealed.
using System;
static class Calculator
{
public static int Square(int x)
{
return x * x;
}
}
Console.WriteLine(Calculator.Square(5));
Output
25
Common uses:
- Utility classes.
- Mathematical functions.
- Helper methods.
- Extension method containers.
29. What is a static constructor?
A static constructor initializes static members of a class. It executes automatically only once before the class is used for the first time.
- Runs only once.
- Cannot have parameters.
- Cannot have access modifiers.
- Cannot be called explicitly.
using System;
class Student
{
static Student()
{
Console.WriteLine("Static Constructor Called");
}
public Student()
{
Console.WriteLine("Object Created");
}
}
Student s1 = new Student();
Student s2 = new Student();
Output
Static Constructor Called
Object Created
Object Created
Explanation:
- The static constructor runs only once when the class is first accessed.
- The instance constructor runs every time a new object is created.
- Static constructors are commonly used to initialize static variables or perform one-time setup operations.
30. What is a destructor (finalizer) and how does it work?
A destructor (also called a finalizer) is a special method that is automatically called by the Garbage Collector (GC) before an object is removed from memory. It is mainly used to release unmanaged resources.
Syntax:
using System;
class Student
{
~Student()
{
Console.WriteLine("Destructor Called");
}
}
Key points:
- Has the same name as the class, prefixed with ~.
- Cannot have parameters or access modifiers.
- Cannot be called directly.
- Executes automatically when the Garbage Collector destroys the object.
- The exact time of execution is not guaranteed.
Note: In modern C#, IDisposable and the using statement are generally preferred over destructors for releasing resources.
31. What are Generics and why are they useful?
Generics allow you to create classes, methods, interfaces, and collections that work with different data types while maintaining compile-time type safety. Advantages of Generics:
- Provides compile-time type checking.
- Eliminates unnecessary type casting.
- Improves code reusability.
- Offers better performance than non-generic collections.
using System;
using System.Collections.Generic;
List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);
Console.WriteLine(numbers[0]);
Output
10
Explanation:
- List<int> accepts only integer values.
- Attempting to add any other data type results in a compile-time error.
- Generics are widely used in collections such as List<T>, Dictionary<TKey, TValue> and Queue<T>.
32. What is the difference between List<T> and ArrayList?
Both List<T> and ArrayList store collections of objects, but List<T> is the preferred choice in modern C# applications.
List<T>
- Generic collection.
- Stores only one specified data type.
- Provides compile-time type checking.
- No boxing or unboxing for value types.
- Better performance.
ArrayList
- Non-generic collection.
- Can store different data types.
- Returns objects that require type casting.
- Boxing and unboxing occur for value types.
- Generally slower than List<T>.
In general, use List<T> for almost all new applications. ArrayList is mainly found in older .NET applications.
33. What is the difference between Dictionary<TKey, TValue> and Hashtable?
Both collections store data as key-value pairs, but Dictionary<TKey, TValue> is type-safe and provides better performance.
Dictionary<TKey, TValue>
- Generic collection.
- Keys and values have fixed data types.
- Provides compile-time type checking.
- Does not require boxing or unboxing.
- Faster and recommended for modern applications.
Hashtable
- Non-generic collection.
- Stores keys and values as object.
- Requires type casting when retrieving values.
- May involve boxing and unboxing for value types.
- Mostly used in older .NET applications.
using System;
using System.Collections.Generic;
Dictionary<int, string> students = new Dictionary<int, string>();
students.Add(1, "Alice");
students.Add(2, "Emma");
Console.WriteLine(students[1]);
Output
Alice
In general, use Dictionary<TKey, TValue> for new applications because it is type-safe, faster, and easier to work with than Hashtable.
34. When would you use HashSet<T>?
HashSet<T> is a generic collection that stores only unique values. Duplicate elements are automatically ignored. Use HashSet<T> when:
- Duplicate values should not be allowed.
- Fast searching is required.
- Membership testing using Contains() is performed frequently.
- Performing set operations such as Union(), Intersect(), or Except().
using System;
using System.Collections.Generic;
HashSet<int> numbers = new HashSet<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(10);
foreach (int item in numbers)
{
Console.WriteLine(item);
}
Output
10
20
Explanation:
- The duplicate value 10 is ignored automatically.
- HashSet<T> is optimized for fast lookups and ensuring uniqueness.
35. What are Stack and Queue collections?
Both Stack<T> and Queue<T> are collection classes used to store and retrieve data, but they follow different ordering principles.
Stack
A Stack follows the LIFO (Last In, First Out) principle, meaning the last element added is the first one removed. Common methods includes:
- Push(): Adds an element.
- Pop(): Removes and returns the top element.
- Peek(): Returns the top element without removing it.
using System;
using System.Collections.Generic;
Stack<int> stack = new Stack<int>();
stack.Push(10);
stack.Push(20);
Console.WriteLine(stack.Pop());
Output
20
Queue
A Queue follows the FIFO (First In, First Out) principle, meaning the first element added is the first one removed. Common methods includes:
- Enqueue(): Adds an element.
- Dequeue(): Removes and returns the first element.
- Peek(): Returns the first element without removing it.
using System;
using System.Collections.Generic;
Queue<int> queue = new Queue<int>();
queue.Enqueue(10);
queue.Enqueue(20);
Console.WriteLine(queue.Dequeue());
Output
10
36. What is Exception Handling in C#?
Exception handling is a mechanism used to detect and handle runtime errors without terminating the program unexpectedly. It is implemented using the try, catch, and optionally the finally blocks.
Example: The example below catches an exception caused by division by zero.
using System;
try
{
int number = 10;
int divisor = 0;
int result = number / divisor;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Output
Attempted to divide by zero.
Benefits of exception handling:
- Prevents unexpected application crashes.
- Allows graceful handling of runtime errors.
- Makes debugging easier by providing error information.
- Improves the reliability and stability of applications.
37. What is the purpose of the finally block?
The finally block contains code that always executes, whether an exception occurs or not. It is mainly used to release resources such as files, database connections, or network connections.
Example: The example below shows that the finally block executes even when no exception occurs.
using System;
try
{
Console.WriteLine("Inside Try");
}
catch
{
Console.WriteLine("Inside Catch");
}
finally
{
Console.WriteLine("Cleaning Resources");
}
Output
Inside Try
Cleaning Resources
Common uses of finally:
- Closing files.
- Closing database connections.
- Releasing unmanaged resources.
- Performing cleanup operations before the program exits or continues.
38. How do you create and use a Custom Exception?
A custom exception is a user-defined exception class that inherits from the Exception class. It is used to represent application-specific errors, making error handling more meaningful.
Example: The example below creates and throws a custom exception when the age is less than 18.
using System;
class InvalidAgeException : Exception
{
public InvalidAgeException(string message) : base(message)
{
}
}
try
{
int age = 15;
if (age < 18)
throw new InvalidAgeException("Age must be 18 or above.");
}
catch (InvalidAgeException ex)
{
Console.WriteLine(ex.Message);
}
Output
Age must be 18 or above.
Use custom exceptions when:
- Representing business-specific errors.
- Making error messages more meaningful.
- Handling application-specific scenarios separately.
39. What is LINQ?
LINQ (Language Integrated Query) is a feature in C# that allows querying and manipulating data using a consistent syntax. It can work with collections, arrays, XML, databases, and many other data sources.

Example: The example below retrieves numbers greater than 20 using the Where() method.
using System;
using System.Linq;
int[] numbers = { 10, 25, 15, 40 };
var result = numbers.Where(n => n > 20);
foreach (var item in result)
{
Console.WriteLine(item);
}
Output
25
40
Advantages of LINQ:
- Makes code shorter and more readable.
- Reduces manual loops.
- Provides powerful filtering and sorting capabilities.
- Supports multiple data sources.
40. What is the difference between Query Syntax and Method Syntax in LINQ?
Both Query Syntax and Method Syntax are used to write LINQ queries. They produce the same result but differ in their syntax and style.
Query Syntax
- Similar to SQL syntax.
- Easier to read for complex queries.
- Uses keywords such as from, where, and select.
using System;
using System.Linq;
int[] numbers = { 10, 25, 15, 40 };
var result =
from n in numbers
where n > 20
select n;
foreach (var item in result)
{
Console.WriteLine(item);
}
Output
25
40
Method Syntax
- Uses extension methods.
- More commonly used in modern C# applications.
- Supports all LINQ operations.
using System;
using System.Linq;
int[] numbers = { 10, 25, 15, 40 };
var result = numbers.Where(n => n > 20);
foreach (var item in result)
{
Console.WriteLine(item);
}
Output
25
40
In practice, use Query Syntax if you prefer SQL-like queries and use Method Syntax for greater flexibility and because it is the preferred approach in most modern C# projects.
41. What are the most commonly used LINQ methods?
LINQ provides many methods for querying and manipulating data. Some of the most commonly used methods are:
- Where(): Filters data.
- Select(): Projects specific values.
- OrderBy(): Sorts data in ascending order.
- OrderByDescending(): Sorts data in descending order.
- First() / FirstOrDefault(): Returns the first matching element.
- Any(): Checks whether any element satisfies a condition.
- Count(): Returns the number of elements.
- Distinct(): Removes duplicate values.
- GroupBy(): Groups elements based on a key.
Example: The example below filters and sorts the collection.
using System;
using System.Linq;
int[] numbers = { 25, 5, 30, 15, 10 };
var result = numbers
.Where(n => n > 10)
.OrderBy(n => n);
foreach (var item in result)
{
Console.WriteLine(item);
}
Output
15
25
30
42. What are Extension Methods?
Extension methods allow you to add new methods to an existing class without modifying or inheriting from it. They are defined as static methods inside a static class, with the first parameter preceded by the this keyword.
Example: The example below adds a new method named Greeting() to the string class.
using System;
public static class StringExtension
{
public static string Greeting(this string name)
{
return "Hello " + name;
}
}
Console.WriteLine("Harry".Greeting());
Output
Hello Harry
Benefits:
- Improves code readability.
- Extends existing classes without modifying their source code.
- Widely used by LINQ and many .NET libraries.
43. What are Anonymous Types?
Anonymous types allow you to create temporary objects without explicitly defining a class. The compiler automatically generates the type based on the properties provided.
Example: The example below creates an anonymous object with two properties.
using System;
var student = new
{
Id = 101,
Name = "Sam"
};
Console.WriteLine(student.Name);
Output
Sam
Anonymous types are commonly used for:
- Storing LINQ query results.
- Creating temporary data objects.
- Returning projected or grouped data.
- Passing related values without creating a separate class.
44. What is the using statement used for?
using statement automatically releases resources when they are no longer needed. It is commonly used with objects that implement the IDisposable interface, such as files, streams, and database connections.
Example: The example below writes data to a file. After the using block finishes, the file is automatically closed.
using System;
using System.IO;
using (StreamWriter writer = new StreamWriter("sample.txt"))
{
writer.WriteLine("Hello World");
}
Console.WriteLine("File written successfully.");
Output
File written successfully.
Common uses of using:
- Working with files.
- Database connections.
- Network streams.
- File streams and memory streams.
- Any object that implements IDisposable.
Note: Even if an exception occurs inside the using block, the object's Dispose() method is automatically called, ensuring resources are released properly.
45. What are Delegates in C#?
A delegate is a type-safe reference that can store the reference of one or more methods with the same signature. It allows methods to be treated as objects, making it possible to pass methods as arguments or invoke them dynamically.
Example: The example below creates a delegate that points to the Display() method.
using System;
delegate void Message();
class Program
{
static void Display()
{
Console.WriteLine("Hello World");
}
static void Main()
{
Message msg = Display;
msg();
}
}
Output
Hello World
Common uses of delegates:
- Event handling.
- Callback methods.
- Anonymous methods.
- Lambda expressions.
46. What is the difference between a Delegate and an Event?
Both delegates and events are used for communication between objects, but an event provides controlled access to a delegate.
Delegate
- Can be invoked from anywhere it is accessible.
- Can reference one or more methods.
- Used for callbacks and passing methods as parameters.
Event
- Built on top of delegates.
- Can only be raised (invoked) by the class that declares it.
- Used to notify subscribers when an action or state change occurs.
- Provides better encapsulation than delegates.
In general, use a delegate when methods need to be passed as parameters and use an event when notifying multiple subscribers about an action.
47. What are Multicast Delegates?
A multicast delegate is a delegate that references multiple methods. When invoked, it executes all the referenced methods in the order they were added.
Example: The example below invokes two methods using a single delegate.
using System;
delegate void Message();
class Program
{
static void Hello()
{
Console.WriteLine("Hello");
}
static void Welcome()
{
Console.WriteLine("Welcome");
}
static void Main()
{
Message msg = Hello;
msg += Welcome;
msg();
}
}
Output
Hello
Welcome
Common uses of multicast delegates:
- Event handling.
- Executing multiple callback methods.
- Notifying multiple subscribers with a single invocation.
48. What are Lambda Expressions?
A lambda expression is a concise way to write an anonymous function. It uses the => operator and is widely used with delegates, LINQ, and events.
Syntax:
(parameters) => expression
Example: The example below creates a lambda expression that calculates the square of a number.
using System;
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
Output
25
Benefits:
- Reduce the amount of code.
- Improve readability.
- Frequently used with LINQ.
- Eliminate the need to create separate methods for simple operations.
49. What are Func, Action and Predicate delegates?
Func, Action and Predicate are built-in generic delegates provided by .NET that simplify working with methods and lambda expressions.
Func
- Returns a value.
- Can accept zero or more input parameters.
- The last generic type specifies the return type.
using System;
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(10, 20));
Output
30
Action
- Does not return a value (void).
- Used for methods that perform an action.
using System;
Action<string> greet = name =>
Console.WriteLine("Hello " + name);
greet("Rock");
Output
Hello Rock
Predicate
- Accepts one input parameter.
- Always returns a bool.
- Commonly used for testing conditions.
using System;
Predicate<int> isEven = x => x % 2 == 0;
Console.WriteLine(isEven(8));
Output
True
50. What is Reflection?
Reflection is a feature that allows a program to inspect and retrieve information about its own types at runtime. It can access metadata such as classes, methods, properties, constructors and assemblies.
Example: The example below retrieves the name of the string type.
using System;
Console.WriteLine(typeof(string).Name);
Output
String
Common uses of Reflection:
- Loading assemblies dynamically.
- Creating objects at runtime.
- Accessing type metadata.
- Dependency Injection frameworks.
- Serialization libraries.
51. What are Attributes in C#?
Attributes are special metadata that provide additional information about classes, methods, properties, assemblies, and other program elements. They are used by the compiler and runtime but do not directly affect the program's logic.
Example: The example below marks a method as obsolete.
using System;
class Program
{
[Obsolete("Use NewMethod instead.")]
static void OldMethod()
{
Console.WriteLine("Old Method");
}
static void Main()
{
OldMethod();
}
}
Output
Compiler Warning:
'Program.OldMethod()' is obsolete: 'Use NewMethod instead.'
Common built-in attributes:
- [Obsolete]: Marks outdated code.
- [Serializable]: Indicates that a class can be serialized.
- [Conditional]: Executes methods only under specific conditions.
- [DllImport]: Imports unmanaged functions from DLLs.
Attributes are widely used in ASP.NET, Entity Framework, serialization, validation, testing frameworks, and custom libraries.
52. What are Nullable Value Types and Nullable Reference Types?
Nullable types allow variables to hold null when appropriate, helping write safer and more reliable code.
Nullable Value Types
Normally, value types such as int, double, and bool cannot store null. By appending ?, they can hold either a value or null.
using System;
int? age = null;
Console.WriteLine(age.HasValue);
Output
False
Nullable Reference Types
Introduced in C# 8.0, nullable reference types help detect possible null reference errors during compilation.
using System;
string? name = null;
Console.WriteLine(name == null);
Output
True
Benefits:
- Reduces NullReferenceException.
- Improves code safety.
- Makes null handling more explicit.
- Helps the compiler warn about possible null-related issues.
53. What is Pattern Matching in C#?
Pattern matching provides a concise way to check an object's type or value and execute code based on the result. It makes conditional logic shorter and easier to read.
Example: The example below checks whether an object is an integer and stores it in a variable.
using System;
object value = 25;
if (value is int number)
{
Console.WriteLine(number * 2);
}
Output
50
Pattern matching is commonly used with:
- is
- switch
- switch expressions
54. What are the is and as operators?
Both operators are used for type checking and type conversion, but they behave differently.
is Operator
- Checks whether an object is of a specified type.
- Returns true or false.
- Can also perform pattern matching.
using System;
object obj = "Hello";
Console.WriteLine(obj is string);
Output
True
as Operator
- Safely converts an object to a specified reference or nullable type.
- Returns null if the conversion fails instead of throwing an exception.
using System;
object obj = "Hello";
string? text = obj as string;
Console.WriteLine(text);
Output
Hello
In general, use to check an object's type and use as for safe type conversion without risking an exception.
55. What is the difference between == and .Equals()?
Both are used to compare objects, but they perform comparisons differently.
== Operator
- Compares values for value types.
- By default, compares references for reference types (unless overloaded).
- Can be overloaded to provide custom comparison behavior.
.Equals() Method
- Compares the logical equality or contents of objects.
- Can be overridden to define custom comparison logic.
- Commonly used when comparing object values.
using System;
string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 == s2);
Console.WriteLine(s1.Equals(s2));
Output
True
True
Note: For string, both == and .Equals() compare the actual text because the == operator is overloaded for the String class.
56. What is Garbage Collection (GC)?
Garbage Collection (GC) is an automatic memory management feature of .NET that reclaims memory occupied by objects that are no longer in use. Developers do not need to manually free memory.
Example: In the example below, the object becomes eligible for garbage collection after its reference is removed.
using System;
class Student
{
}
Student s = new Student();
s = null;
Key points:
- Automatically frees unused memory.
- Helps prevent memory leaks.
- Eliminates the need for manual memory management.
- Reduces programming errors caused by improper memory handling.
Note: The exact time when the Garbage Collector runs is determined by the .NET runtime and cannot be controlled directly.
57. What is the IDisposable interface?
IDisposable is an interface used to release unmanaged resources, such as files, database connections, and network streams, when they are no longer needed. A class implementing IDisposable must provide a Dispose() method.
Example: The example below releases resources automatically by using the using statement.
using System;
class Sample : IDisposable
{
public void Dispose()
{
Console.WriteLine("Resources Released");
}
}
using (Sample s = new Sample())
{
}
Output
Resources Released
Key points:
- Used to release unmanaged resources.
- Requires implementation of the Dispose() method.
- Commonly used with the using statement.
- Ensures resources are released even if an exception occurs.
58. What is the difference between Dispose() and Finalize()?
Both Dispose() and Finalize() are used for resource cleanup, but they work differently.
Dispose()
- Defined by the IDisposable interface.
- Called explicitly by the programmer or automatically by the using statement.
- Releases unmanaged resources immediately.
- Recommended approach for resource cleanup.
Finalize()
- Also known as a destructor.
- Called automatically by the Garbage Collector.
- Execution time is not predictable.
- Used as a backup if Dispose() was not called.
In general, prefer Dispose() for releasing resources and use Finalize() only when unmanaged resources require additional cleanup if Dispose() was missed.
59. What is Multithreading?
Multithreading is the ability of a program to execute multiple threads concurrently within the same process. It improves application responsiveness and allows better utilization of CPU resources.
Example: The example below creates and starts a new thread.
using System;
using System.Threading;
Thread thread = new Thread(() =>
{
Console.WriteLine("New Thread Running");
});
thread.Start();
Output
New Thread Running
Common uses of multithreading:
- Background processing.
- File downloading.
- Image and video processing.
- Running multiple independent tasks simultaneously.
- Keeping the user interface responsive during long-running operations.
60. What is the difference between a Thread and a Task?
Both Thread and Task are used to perform work concurrently, but they operate at different levels.
Thread
- Represents an actual operating system thread.
- Created using the Thread class.
- Provides low-level control over thread execution.
- Requires manual creation and management.
Task
- Represents an asynchronous operation.
- Uses the .NET Thread Pool internally.
- Easier to create and manage.
- Recommended for most modern applications.
Example: The example below runs a task asynchronously.
using System;
using System.Threading.Tasks;
Task.Run(() =>
{
Console.WriteLine("Task Running");
});
Output
Task Running
In general, use Task for most asynchronous programming and use Thread only when low-level thread control is required.
61. What are async and await?
The async and await keywords simplify asynchronous programming by allowing long-running operations to execute without blocking the calling thread.
- async marks a method as asynchronous.
- await pauses the method until the awaited task completes.
Example: The example below waits for two seconds before printing a message.
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
await Task.Delay(2000);
Console.WriteLine("Task Completed");
}
}
Output
Task Completed
Benefits:
- Improves application responsiveness.
- Prevents UI freezing.
- Makes asynchronous code easier to read and maintain.
- Eliminates the need for complex callback logic.
62. What is a Race Condition?
A race condition occurs when two or more threads access and modify the same shared resource simultaneously, causing unpredictable or incorrect results.
Example: In the example below, both threads increment the same variable simultaneously.
int counter = 0;
// Thread 1
counter++;
// Thread 2
counter++;
The final value of counter may not be what you expect because both threads can execute at the same time. Race conditions can lead to:
- Incorrect or inconsistent data.
- Unexpected program behavior.
- Difficult-to-debug issues.
- Application crashes.
63. What is Thread Synchronization and how does the lock keyword work?
Thread synchronization ensures that only one thread can access a shared resource at a time. The lock keyword creates a critical section, preventing multiple threads from executing the same block of code simultaneously.
Example: The example below safely increments a shared variable using lock.
using System;
class Counter
{
private readonly object obj = new object();
private int count = 0;
public void Increment()
{
lock (obj)
{
count++;
}
}
public void Display()
{
Console.WriteLine(count);
}
}
class Program
{
static void Main()
{
Counter counter = new Counter();
counter.Increment();
counter.Increment();
counter.Display();
}
}
Output
2
Benefits of lock:
- Prevents race conditions.
- Ensures thread safety.
- Protects shared data from concurrent access.
- Allows only one thread to execute the locked code block at a time.
64. What are the SOLID principles?
SOLID is a set of five object-oriented design principles that help developers write maintainable, scalable and loosely coupled software.
- S - Single Responsibility Principle (SRP): A class should have only one reason to change.
- O - Open/Closed Principle (OCP): Software should be open for extension but closed for modification.
- L - Liskov Substitution Principle (LSP): Derived classes should be replaceable with their base classes without affecting correctness.
- I - Interface Segregation Principle (ISP): Clients should not be forced to depend on interfaces they do not use.
- D - Dependency Inversion Principle (DIP): Depend on abstractions rather than concrete implementations.
Benefits of SOLID principles:
- Improves code readability.
- Reduces coupling between classes.
- Makes applications easier to maintain and extend.
- Simplifies testing and debugging.
65. Explain the architecture of a typical .NET application.
A typical .NET application follows a layered architecture, where each layer has a specific responsibility. This improves code organization, maintainability, and scalability.
Presentation Layer
- Handles user interaction.
- Receives user requests and displays results.
- Examples: ASP.NET Core MVC, Blazor, Windows Forms, WPF.
Business Logic Layer (BLL)
- Contains the application's business rules.
- Validates and processes data received from the presentation layer.
- Acts as the bridge between the UI and the database.
Data Access Layer (DAL)
- Communicates with the database.
- Performs CRUD (Create, Read, Update, Delete) operations.
- Common technologies include Entity Framework Core and ADO.NET.
Database
- Stores application data.
- Examples: SQL Server, MySQL, PostgreSQL, Oracle.
Request Flow:
User
↓
Presentation Layer
↓
Business Logic Layer
↓
Data Access Layer
↓
Database
Benefits of layered architecture:
- Separates responsibilities across different layers.
- Makes testing and debugging easier.
- Improves maintainability and code reusability.
- Allows applications to scale more easily as they grow.