Object-Oriented Programming (OOP) in C#
Object-Oriented Programming is a paradigm based on the concept of "objects," which contain data (fields/properties) and code (methods). C# was built from the ground up as a pure OOP language.
1. The Core: Classes & Objects
Classes (The Blueprint)
A Class is a template that defines the structure and behavior of an object.
public class BankAccount
{
// Fields (private by convention)
private string _accountNumber;
// Properties (public way to access data)
public string Owner { get; set; }
public decimal Balance { get; private set; }
// Constructor (Initializes the object)
public BankAccount(string owner, decimal initialBalance)
{
Owner = owner;
Balance = initialBalance;
_accountNumber = Guid.NewGuid().ToString();
}
// Methods (Behavior)
public void Deposit(decimal amount) => Balance += amount;
}
Objects (The Instances)
An Object is a specific instance of a class.
var myAccount = new BankAccount("Alice", 1000m);
myAccount.Deposit(500m);
2. Inheritance & Polymorphism
Inheritance
Allows a class (Child/Derived) to acquire the properties and methods of another class (Parent/Base).
public class SavingAccount : BankAccount
{
public decimal InterestRate { get; set; } = 0.05m;
public SavingAccount(string owner, decimal balance) : base(owner, balance) { }
}
Polymorphism & Virtual Methods
Polymorphism allows objects to be treated as instances of their parent class while keeping their unique behavior. Use virtual in the base class and override in the derived class.
public class Shape
{
public virtual void Draw() => Console.WriteLine("Drawing a shape");
}
public class Circle : Shape
{
public override void Draw() => Console.WriteLine("Drawing a circle");
}
3. Interfaces: Defining Contracts
An Interface defines a set of methods and properties that a class must implement, without specifying how.
Basic Interface
public interface ILogger
{
void Log(string message);
}
public class FileLogger : ILogger
{
public void Log(string message) => File.WriteAllText("log.txt", message);
}
Generic Interfaces
Generic interfaces allow you to define contracts that work with any data type.
public interface IRepository<T>
{
void Add(T item);
T GetById(int id);
}
public class UserRepository : IRepository<User>
{
public void Add(User user) { /* DB logic */ }
public User GetById(int id) => new User();
}
4. Generic Types: Reusable Templates
Generics allow you to write a class that handles any type while maintaining type safety.
public class Result<T>
{
public bool IsSuccess { get; set; }
public T Data { get; set; }
public string Error { get; set; }
}
var userResult = new Result<User> { Data = new User() };
var intResult = new Result<int> { Data = 42 };
5. Partial Classes
The partial keyword allows you to split a single class definition across multiple .cs files. This is common in auto-generated code (like UI designers) or very large classes.
File 1: UserAuth.cs
public partial class User
{
public bool Login() => true;
}
File 2: UserProfile.cs
public partial class User
{
public string Name { get; set; }
}
6. The SOLID Principles
SOLID is an acronym for five design principles that make software more understandable, flexible, and maintainable.
1. Single Responsibility Principle (SRP)
A class should have one, and only one, reason to change.
Bad: A User class that saves itself to the database and sends emails.
Good: A User class for data, a UserRepository for DB, and an EmailService.
2. Open/Closed Principle (OCP)
Classes should be open for extension, but closed for modification. You should be able to add new functionality without changing existing code (usually via interfaces or inheritance).
3. Liskov Substitution Principle (LSP)
Objects of a superclass should be replaceable with objects of its subclasses without breaking the application.
If a Bird class has a Fly() method, but Ostrich inherits from it and throws an error when flying, you are breaking LSP.
4. Interface Segregation Principle (ISP)
Clients should not be forced to depend on methods they do not use.
Split large "fat" interfaces into smaller, more specific ones (e.g., IMovable and IFlyable instead of one big IEntity interface).
5. Dependency Inversion Principle (DIP)
Depend on abstractions (interfaces), not on concreations (classes). High-level modules should not depend on low-level modules. Both should depend on interfaces.
7. Abstract vs. Sealed Classes
abstractClass: A class that cannot be instantiated. It is meant only to be inherited from. It can containabstractmethods with no implementation.sealedClass: A class that cannot be inherited from. This is used for security or performance (e.g.,stringis a sealed class).