1. 🔴 What is the object class in C#, and why is it important?
The object class (System.Object) is the ultimate base class of all types in C#.
Every class, struct, enum, and other .NET type derives directly or indirectly from System.Object.
Important methods of the object class
|
Method |
Purpose |
|---|---|
|
|
Returns a string representation |
|
|
Compares objects for equality |
|
|
Returns a hash code |
|
|
Returns runtime type information |
|
|
Checks whether references point to the same object |
|
|
Creates a shallow copy |
Example
class Employee
{
public string Name { get; set; } = "";
}
Employee emp = new Employee { Name = "John" };
Console.WriteLine(emp.ToString());
Console.WriteLine(emp.GetType().Name);
Console.WriteLine(emp.GetHashCode());
Output (example):
Employee
Employee
54267293
The hash code is not guaranteed to be the same between executions.
Why is object important?
-
Provides common functionality to all .NET types.
-
Supports polymorphism.
-
Enables general-purpose APIs.
-
Provides the foundation for equality and hashing.
-
Supports runtime type inspection.
Interview key point: System.Object is the root of the .NET type system, and all C# types inherit its fundamental behavior.
2. 🔴 What is the difference between ==, Equals(), and ReferenceEquals()?
These are used to compare objects, but their behavior differs.
-
==– An operator whose behavior depends on the operand types and operator overloads. -
Equals()– Compares equality according to the type's implementation. -
ReferenceEquals()– Checks whether two references point to the same object.
Example
class Employee
{
public string Name { get; set; } = "";
}
Employee e1 = new Employee { Name = "John" };
Employee e2 = new Employee { Name = "John" };
Employee e3 = e1;
Console.WriteLine(e1 == e2);
Console.WriteLine(e1.Equals(e2));
Console.WriteLine(ReferenceEquals(e1, e2));
Console.WriteLine(ReferenceEquals(e1, e3));
Output:
False
False
False
True
Explanation:
-
e1ande2are different objects. -
e3references the same object ase1. -
The
Employeeclass does not override equality behavior.
Comparison
|
|
|
|
|---|---|---|
|
Equality operator |
Virtual equality method |
Static method |
|
Can be overloaded |
Can be overridden |
Cannot be overridden |
|
May compare values or references |
May compare values or references |
Compares reference identity |
|
Depends on operand types |
Depends on implementation |
Always checks object identity |
Important exception: String
string s1 = new string("Hello".ToCharArray());
string s2 = new string("Hello".ToCharArray());
Console.WriteLine(s1 == s2); // True
Console.WriteLine(s1.Equals(s2)); // True
Console.WriteLine(ReferenceEquals(s1, s2)); // False
Key point: For ordinary classes without equality customization, == and Equals() normally compare references. For types such as string, they compare values.
3. 🔴 What is boxing and unboxing?
Boxing converts a value type into an object or an implemented interface reference, generally allocating an object on the managed heap.
Unboxing extracts the value type from a boxed object through an explicit conversion.
Example
int number = 100;
// Boxing
object obj = number;
// Unboxing
int result = (int)obj;
Console.WriteLine(result); // 100
Execution flow
int number = 100
|
v
Boxing
|
v
object obj = number
|
v
Boxed copy stored in managed heap
|
v
Unboxing
|
v
int result = (int)obj
Important points
-
Boxing generally involves heap allocation and copying the value.
-
Unboxing requires a compatible value type.
-
Incorrect unboxing can throw
InvalidCastException. -
Excessive boxing/unboxing can affect performance.
Example: Incorrect unboxing
object obj = 100;
// InvalidCastException
double value = (double)obj;
Correct approach:
int value = (int)obj;
double result = value;
How to avoid boxing?
Use generics:
List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);
Unlike a nongeneric ArrayList, List<int> does not require boxing integers when adding them.
Interview key point: Boxing converts a value type to an object representation; unboxing retrieves the value. Generics help avoid unnecessary boxing.
4. 🔴 What is the difference between shallow copy and deep copy?
Shallow copy creates a new object but copies references to nested objects.
Deep copy creates a new object and independent copies of the nested mutable objects that need to be duplicated.
Example: Shallow copy
class Address
{
public string City { get; set; } = "";
}
class Employee
{
public string Name { get; set; } = "";
public Address Address { get; set; } = new();
public Employee ShallowCopy()
{
return (Employee)MemberwiseClone();
}
}
Usage:
Employee e1 = new Employee
{
Name = "John",
Address = new Address { City = "Chennai" }
};
Employee e2 = e1.ShallowCopy();
e2.Name = "David";
e2.Address.City = "Coimbatore";
Console.WriteLine(e1.Name); // John
Console.WriteLine(e1.Address.City); // Coimbatore
Explanation:
-
Nameis a string, and assigning a new string toe2.Namedoes not changee1.Name. -
Addressis a reference type, so both objects share the sameAddressinstance.
Example: Deep copy
Add this method to Employee:
public Employee DeepCopy()
{
return new Employee
{
Name = this.Name,
Address = new Address
{
City = this.Address.City
}
};
}
Usage:
Employee e2 = e1.DeepCopy();
e2.Address.City = "Madurai";
Console.WriteLine(e1.Address.City); // Chennai
Console.WriteLine(e2.Address.City); // Madurai
Assuming e1.Address.City was initially Chennai.
Comparison
|
Shallow copy |
Deep copy |
|---|---|
|
Creates a new outer object |
Creates independent object graph as needed |
|
Nested references are shared |
Nested mutable objects are copied |
|
Usually faster |
Usually more expensive |
|
Less memory allocation |
More memory allocation |
|
Changes to shared nested objects are visible in both |
Changes to copied nested objects remain independent |
Key point: Shallow copy duplicates the outer object, while deep copy duplicates nested mutable state to achieve independence.
5. 🟡 What is the purpose of GetHashCode()?
GetHashCode() returns an integer hash code used by hash-based collections to organize and locate objects efficiently.
It is commonly used by:
-
Dictionary<TKey, TValue> -
HashSet<T> -
Hashtable
Example
string name = "John";
int hash = name.GetHashCode();
Console.WriteLine(hash);
Real-world example
Dictionary<string, int> employees = new();
employees.Add("John", 1001);
employees.Add("David", 1002);
Console.WriteLine(employees["John"]);
Output:
1001
The dictionary uses hashing and equality comparisons to find the matching key.
Important rules
-
Equal objects must produce equal hash codes.
-
Different objects can produce the same hash code.
-
Hash codes are not guaranteed to remain the same across application executions.
-
Hash codes should remain stable while an object is used as a hash-based collection key.
-
Hash codes are not suitable for encryption or secure identification.
Key point: GetHashCode() improves lookup efficiency in hash-based collections, but it does not uniquely identify an object.
6. 🟡 Why should Equals() and GetHashCode() be overridden together?
When two objects are considered equal by Equals(), they must return the same hash code.
Otherwise, hash-based collections may behave incorrectly.
Example
class Employee
{
public int Id { get; set; }
public string Name { get; set; } = "";
public override bool Equals(object? obj)
{
return obj is Employee emp && Id == emp.Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Usage:
Employee e1 = new Employee { Id = 1, Name = "John" };
Employee e2 = new Employee { Id = 1, Name = "David" };
Console.WriteLine(e1.Equals(e2)); // True
Console.WriteLine(
e1.GetHashCode() == e2.GetHashCode()
); // True
Here, equality is based on Id, not Name.
Why is it important?
Consider:
HashSet<Employee> employees = new();
employees.Add(e1);
employees.Add(e2);
Console.WriteLine(employees.Count); // 1
Both employees are treated as equal because they have the same Id.
Important rules
-
If
Equals()returnstrue, hash codes must match. -
Equal hash codes do not guarantee equality.
-
When customizing equality for a class, also consider implementing
IEquatable<T>and defining consistent==and!=operators. -
Avoid changing fields used in hashing while an object is stored in a
HashSetor used as a dictionary key.
Key point: Override Equals() and GetHashCode() together to maintain consistent equality behavior in collections.
7. 🟡 What is the difference between MemberwiseClone() and a custom deep copy?
MemberwiseClone() is a protected method inherited from System.Object that creates a shallow copy.
A custom deep-copy method explicitly creates independent copies of nested objects.
Example
class Employee
{
public string Name { get; set; } = "";
public Employee Clone()
{
return (Employee)MemberwiseClone();
}
}
Usage:
Employee e1 = new Employee { Name = "John" };
Employee e2 = e1.Clone();
Console.WriteLine(ReferenceEquals(e1, e2)); // False
Comparison
|
|
Custom deep copy |
|---|---|
|
Built-in method |
Developer-defined implementation |
|
Creates shallow copy |
Creates deep copy |
|
Copies field values |
Copies selected nested objects |
|
Shares nested object references |
Can create independent nested objects |
|
Less code |
Requires explicit copying logic |
|
Protected method |
Access depends on implementation |
Important: MemberwiseClone() copies fields, including backing fields of auto-properties. It does not recursively clone referenced objects.
Key point: Use MemberwiseClone() for shallow copying; implement a custom deep copy when nested mutable objects must be independent.
8. 🟡 What is the difference between stack and heap memory in C#?
C# applications use stack and heap memory for different purposes.
-
Stack – Primarily stores method call frames, local variables, and execution-related information.
-
Heap – Stores dynamically allocated objects managed by the Garbage Collector.
Example
class Employee
{
public int Id { get; set; }
}
void Test()
{
int number = 100;
Employee emp = new Employee();
emp.Id = 10;
}
Conceptual memory representation
STACK MANAGED HEAP
------------------ ------------------
Test() frame
number = 100
emp (reference) ------------> Employee Object
Id = 10
------------------ ------------------
This is a conceptual example. The JIT compiler can optimize storage, so the exact physical layout is not guaranteed.
Comparison
|
Stack |
Heap |
|---|---|
|
Stores execution frames and locals |
Stores dynamically allocated objects |
|
Very efficient allocation |
Managed allocation with GC |
|
Frame lifetime tied to method execution |
Object lifetime depends on reachability |
|
Limited stack space |
Larger managed memory area |
|
Automatically unwinds on return |
GC reclaims unreachable objects |
Important interview clarification
Value types are not always stored on the stack.
For example:
class Employee
{
public int Age { get; set; }
}
Age is a value type, but it is part of the Employee object, which is normally allocated on the managed heap.
Similarly, a struct can be stored inside a heap-allocated object or array.
Key point: Value types and reference types describe value semantics and reference semantics, not an absolute rule about stack versus heap allocation.
9. 🟢 How does the Garbage Collector manage object memory?
The Garbage Collector (GC) automatically manages managed heap memory in .NET.
It identifies objects that are no longer reachable from application roots and reclaims their memory.
Example
class Employee
{
public string Name { get; set; } = "";
}
void CreateEmployee()
{
Employee emp = new Employee
{
Name = "John"
};
}
Once CreateEmployee() completes, the object can become eligible for garbage collection if no reachable references remain.
Garbage Collection flow
Object Created
|
v
Allocated on Managed Heap
|
v
Application Uses Object
|
v
Object Becomes Unreachable
|
v
Garbage Collector Runs
|
v
Identifies Reachable Objects
|
v
Reclaims Unreachable Memory
|
v
May Compact Heap
GC generations
.NET uses generational garbage collection.
|
Generation |
Purpose |
|---|---|
|
Generation 0 |
Newly allocated, short-lived objects |
|
Generation 1 |
Intermediate objects that survived collection |
|
Generation 2 |
Longer-lived objects |
|
Large Object Heap (LOH) |
Large allocations, generally 85 KB or more |
Important points
-
GC automatically manages managed memory.
-
Objects are collected based on reachability, not simply because a method has ended.
-
GC uses generations to improve performance.
-
GC does not guarantee immediate cleanup.
-
GC.Collect()can request collection, but should normally be avoided in application code. -
Dispose()is still required for timely release of disposable resources.
Key point: Garbage Collection manages memory automatically, while IDisposable supports deterministic resource cleanup.
10. 🟢 What are weak references in C#?
A weak reference allows an application to reference an object without preventing the Garbage Collector from collecting it.
Normally, a strongly reachable object remains alive. A weak reference does not keep its target alive by itself.
Example
class Employee
{
public string Name { get; set; } = "";
}
Employee emp = new Employee { Name = "John" };
WeakReference<Employee> weakRef = new(emp);
// Remove strong reference
emp = null!;
if (weakRef.TryGetTarget(out Employee? target))
{
Console.WriteLine(target.Name);
}
else
{
Console.WriteLine("Object collected");
}
Explanation:
-
WeakReference<T>holds a weak reference. -
TryGetTarget()checks whether the object is still available. -
If GC has collected the object,
TryGetTarget()returnsfalse. -
The object may still be available if garbage collection has not occurred.
Strong reference vs weak reference
|
Strong reference |
Weak reference |
|---|---|
|
Keeps object reachable |
Does not prevent collection |
|
Normal object reference |
Uses |
|
Object stays alive while strongly reachable |
Object may be collected |
|
Used in normal application logic |
Used in specialized memory-sensitive scenarios |
Real-world use cases
-
Caching objects that can be recreated.
-
Holding optional references to large objects.
-
Memory-sensitive applications.
-
Specialized object-tracking scenarios.
For most ASP.NET Core caching requirements, IMemoryCache or distributed caching is preferable to implementing caching with weak references.
Key point: Weak references allow access to an object without extending its lifetime.
Quick Interview Revision
|
Concept |
Key point |
|---|---|
|
|
Base class of all C# types |
|
|
Type-dependent equality operator |
|
|
Compares equality based on implementation |
|
|
Checks object identity |
|
Boxing |
Value type → Object representation |
|
Unboxing |
Boxed object → Value type |
|
Shallow copy |
Nested references shared |
|
Deep copy |
Independent nested mutable objects |
|
|
Supports hash-based collections |
|
|
Must maintain equality consistency |
|
|
Creates shallow copy |
|
Stack |
Execution frames and local storage |
|
Heap |
Dynamically allocated objects |
|
Garbage Collector |
Reclaims unreachable managed objects |
|
Gen 0, 1, 2 |
Generational garbage collection |
|
Weak reference |
Does not prevent object collection |
Interview Priority – Senior .NET Developer / Technical Lead
|
Question |
Priority |
|---|---|
|
|
🔴 Must know |
|
|
🔴 Must know |
|
Boxing and unboxing |
🔴 Must know |
|
Shallow copy vs deep copy |
🔴 Must know |
|
|
🔴 Must know |
|
Stack vs heap memory |
🔴 Must know |
|
Garbage Collection and generations |
🔴 Must know |
|
|
🟡 Should know |
|
Weak references |
🟢 Nice to know |
Technical Lead interview focus: Pay particular attention to equality and hashing in Dictionary/HashSet, avoiding boxing in performance-critical code, managed memory behavior, and the difference between Garbage Collection and deterministic resource cleanup using IDisposable.