The C# Fundamentals Every Interviewer Asks
Your Guide to C# and .NET Core Fundamentals
In any C# interview, the interviewer's first goal is to verify your foundational knowledge. They want to know that you don't just use C#, but that you understand how it works under the hood.
This means understanding memory, Object-Oriented Programming (OOP) principles, and the core building blocks of the language.
The questions in this area are designed to test your understanding of 'why' you do things, not just 'how'.
The 'Must-Know' Core Concepts:
- Memory: What is the Stack and the Heap? (
classvs.struct) - OOP Pillars: What's the difference between an
interfaceand anabstract class? - OOP Design: Can you explain the SOLID principles?
- Language Keywords: What is the difference between
readonlyandconst?
If you can answer these questions confidently, you'll establish a strong foundation for the rest of the interview. This cluster will break down each of these key topics.
Stack vs. Heap: A Deep Dive into C# Memory
Interview Question: 'Can you explain the Stack and the Heap?'
This is a fundamental .NET question. Your answer demonstrates your understanding of how .NET manages memory, which is critical for performance.
The simple answer: 'The Stack and the Heap are the two places .NET stores data in memory. The Stack is a fast, simple 'LIFO' (Last-In, First-Out) data structure for static memory allocation. It stores value types (like int, bool, structs) and pointers. The Heap is a larger, more complex area for dynamic memory allocation. It stores reference types (like class objects, strings, and arrays).'
1. The Stack (Fast & Simple)
- What it is: A small, highly efficient block of memory.
- How it works: When a method is called, a 'stack frame' is pushed onto the top of the stack. This frame holds all the local variables for that method. When the method finishes, its frame is 'popped' off the stack, and all its memory is instantly freed.
- Stored here: Value Types (
int,double,bool,char,structs,enums) and pointers/references. - Analogy: A stack of plates. You can only add or remove from the top. It's very fast.
2. The Heap (Large & Dynamic)
- What it is: A large, unstructured region of memory.
- How it works: When you create an object (e.g.,
new MyClass()), .NET finds a free block of memory in the Heap to store it. It then places a pointer (the memory address) to that object on the Stack. - Stored here: Reference Types (
classinstances,string,object, arrays, delegates). - Cleanup: Memory on the Heap is not freed when a method ends. It is cleaned up by the Garbage Collector (GC), which periodically checks for objects on the Heap that no longer have any references pointing to them.
Code Example: Putting it Together
public class MyPerson {
public string Name { get; set; } // 'Name' (string) will be on the Heap
}
public void DoWork() {
// 1. 'age' is an 'int' (Value Type).
// It is stored directly on the Stack.
int age = 30;
// 2. 'personObject' is a 'MyPerson' (Reference Type).
// The object data (with its 'Name' property) is created on the Heap.
// The pointer 'personObject' is stored on the Stack.
MyPerson personObject = new MyPerson() { Name = "Alice" };
// 3. 'anotherAge' is a Value Type. 'age' is copied.
int anotherAge = age;
anotherAge = 31; // 'age' is still 30.
// 4. 'anotherPerson' is a Reference Type. The pointer is copied.
MyPerson anotherPerson = personObject;
anotherPerson.Name = "Bob"; // This changes the same object on the Heap.
// 'personObject.Name' is now also 'Bob'.
} // When DoWork() ends, its stack frame (age, personObject, anotherAge, anotherPerson) is popped.
// The 'MyPerson' object on the Heap is now orphaned and will be cleaned up by the GC.Interface vs. Abstract Class: Which to Choose?
Interview Question: 'When would you use an interface vs. an abstract class?'
This is a classic OOP design question. Your answer should focus on 'has-a' vs. 'is-a' relationships and the intent behind your design.
The simple answer: 'An abstract class defines what an object is (an 'is-a' relationship) and can provide shared implementation. A class can only inherit from one. An interface defines what an object can do (a 'has-a' or 'can-do' capability) and provides no implementation (in classic C#). A class can implement many interfaces.'
This table summarizes the key differences:
| Feature | Abstract Class | Interface | |---|---|---| | Inheritance | A class can inherit from only one. | A class can implement many. | | Implementation | Can provide method implementations. | Traditionally, cannot (pre-C# 8). | | Fields/State | Can have instance fields. | Cannot have instance fields. | | Access Modifiers | Can usepublic, protected, private. | All members are implicitly public. |
| Purpose | To share common code among related classes. (e.g., Animal) | To define a 'contract' for a capability. (e.g., IWalkable, ISwimmable) |
Example: Abstract Class (The 'is-a' Relationship)
Use an abstract class when you have a base class that needs to share code. A Dog 'is-a' Animal. A Cat 'is-a' Animal. They share a common Name property and Eat() method.
// Defines the identity of an Animal
public abstract class Animal {
// Can have fields and implemented properties
public string Name { get; set; }
// Can have a fully implemented method
public void Eat() {
Console.WriteLine($"{Name} is eating.");
}
// Can also have an abstract method that must be overridden
public abstract void MakeSound();
}
// A Dog 'is-a' Animal
public class Dog : Animal {
public override void MakeSound() {
Console.WriteLine("Woof!");
}
}Example: Interface (The 'can-do' Capability)
Use an interface to define a capability that can be applied to many different types of classes. A Dog 'can-do' IWalkable. A Person 'can-do' IWalkable. A Robot 'can-do' IWalkable. These classes are not related, but they share a capability.
// Defines the capability of walking
public interface IWalkable {
// Just the contract, no implementation
void Walk();
int NumberOfLegs { get; }
}
// A Dog implements IWalkable
public class Dog : Animal, IWalkable {
public int NumberOfLegs { get; } = 4;
public void Walk() {
Console.WriteLine("Dog is walking on 4 legs.");
}
public override void MakeSound() {
Console.WriteLine("Woof!");
}
}
// A Robot is not an Animal, but it also implements IWalkable
public class Robot : IWalkable {
public int NumberOfLegs { get; } = 2;
public void Walk() {
Console.WriteLine("Robot is rolling on 2 wheels.");
}
}Final Answer: 'I use an abstract class to create a base for closely related objects. I use an interface to define a contract for a capability that can be shared by unrelated classes. If I can, I prefer to use an interface because it's more flexible.'
Understanding SOLID Principles with Code Examples
Interview Question: 'Can you explain the SOLID principles?'
This is a senior-level OOP design question. Explaining it well shows you care about writing clean, maintainable, and testable code. Just listing the names is not enough; you must explain what they mean.
Answer: 'SOLID is an acronym for five design principles that help create more understandable and flexible software.'
S - Single Responsibility Principle (SRP)
- Concept: A class should have only one reason to change.
- Meaning: A class should do one job, and do it well. Don't create a 'God Class' that handles user logic, database saving, and email sending.
- Bad Example: A
UserServiceclass that also writes to a log file. - Good Example: A
UserServicethat handles user logic, and anILoggerServicethat is injected into it to handle logging. If the logging logic changes (e.g., log to cloud instead of file),UserServicedoes not need to change.
O - Open/Closed Principle (OCP)
- Concept: Software entities (classes, modules) should be open for extension, but closed for modification.
- Meaning: You should be able to add new functionality without changing existing, tested code. This is often achieved with interfaces or abstract classes.
- Bad Example: A
CalculateBonusmethod with a giantif/elseorswitchstatement for each employee type. To add a new employee type, you must modify this method. - Good Example: An
IEmployeeinterface with aCalculateBonus()method. Each employee class (Manager,Developer) implements it. To add aContractor, you just create a new class; no existing code changes.
// OCP Example
public interface IEmployee {
decimal CalculateBonus();
}
public class Developer : IEmployee {
public decimal CalculateBonus() => 1000;
}
public class Manager : IEmployee {
public decimal CalculateBonus() => 2000;
}
// To add a new type, just add a new class. The calculation logic is 'open' to extension.L - Liskov Substitution Principle (LSP)
- Concept: Subtypes must be substitutable for their base types.
- Meaning: A child class should be able to do everything its parent class can do, without breaking things. If you have a method that takes an
Animal, you should be able to pass it aDog(a child ofAnimal) and have it work perfectly. - Bad Example: A
Birdbase class with aFly()method. APenguinchild class inherits fromBird, but it can't fly. If you overrideFly()to throw an exception, you've violated LSP. - Good Example: A
Birdbase class without aFly()method. You would have a separateIFlyableinterface thatSparrowimplements, butPenguindoes not.
I - Interface Segregation Principle (ISP)
- Concept: Clients should not be forced to depend on methods they do not use.
- Meaning: Make your interfaces small and specific. Don't create one giant 'fat' interface.
- Bad Example: One
IWorkerinterface withWork()andEatLunch()methods. ARobotWorkerclass is forced to implementEatLunch(), which it doesn't do (it would throw an exception). - Good Example: Two separate, small interfaces:
IWorkableandIEatable.HumanWorkerimplements both.RobotWorkeronly implementsIWorkable.
D - Dependency Inversion Principle (DIP)
- Concept: High-level modules should not depend on low-level modules. Both should depend on abstractions (interfaces).
- Meaning: Don't 'new up' your dependencies. Instead, 'ask' for them in your constructor (Dependency Injection). This decouples your code.
- Bad Example: A
NotificationService(high-level) creates a new instance ofEmailSender(low-level).NotificationServiceis now hard-coded toEmailSender. You can't test it without sending a real email. - Good Example:
NotificationServicedepends on anIMessageSenderinterface. You inject anEmailSenderclass in production. In a unit test, you can inject aMockMessageSender. The high-level service is 'inverted'—it no longer controls its dependencies.
// DIP Example
public class NotificationService {
private readonly IMessageSender _sender;
// Depends on an abstraction (interface), not a concretion (class)
public NotificationService(IMessageSender sender) {
_sender = sender;
}
public void Notify(string message) {
_sender.SendMessage(message);
}
}C# Fundamentals: struct vs. class Explained
Interview Question: 'What's the difference between a class and a struct?'
This is a C# fundamentals question that will be asked. It tests your understanding of value types vs. reference types and memory allocation.
Key Interview Answer Points:
The shortest, most accurate answer has two parts:
- A
classis a reference type. Astructis a value type. - Class instances are stored on the Heap. Struct instances are typically stored on the Stack (if they are local variables).
This difference in memory has huge implications for performance and behavior.
Code Example: The Assignment Difference
This is the most important concept to show. When you assign a class, you copy the reference. When you assign a struct, you copy the value.
// 1. The Class (Reference Type)
public class MyPointClass {
public int X { get; set; }
}
// 2. The Struct (Value Type)
public struct MyPointStruct {
public int X { get; set; }
}
public void Demonstrate() {
// --- Class Example (Copying a Reference) ---
MyPointClass classA = new MyPointClass() { X = 10 };
MyPointClass classB = classA; // 'classB' now points to the same object as 'classA'
classB.X = 20;
Console.WriteLine(classA.X); // Output: 20
// --- Struct Example (Copying a Value) ---
MyPointStruct structA = new MyPointStruct() { X = 10 };
MyPointStruct structB = structA; // 'structB' is a brand new copy of 'structA'
structB.X = 20;
Console.WriteLine(structA.X); // Output: 10
}
When should you use a struct?
The interviewer might ask for a follow-up. 'When would you actually use one?'
Answer: 'You should default to using a class. You should only use a struct for small, lightweight objects that logically represent a single value, have no complex behavior, and are immutable.'
Good examples for struct:
Point(with X and Y)Color(with R, G, B)KeyValuePair
Using a struct in these cases can be a performance win because you avoid allocating memory on the Heap and putting pressure on the Garbage Collector.


