On this page
OOP in C#
C# is a fully object-oriented language. Classes define data and behavior; interfaces specify contracts; inheritance enables code reuse.
Classes and Objects
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
public virtual void Introduce()
{
Console.WriteLine($"I am {Name}, age {Age}");
}
}
var alice = new Person("Alice", 30);
alice.Introduce();
Inheritance and Override
class Employee : Person
{
public string Department { get; set; }
public Employee(string name, int age, string dept)
: base(name, age)
{
Department = dept;
}
public override void Introduce()
{
base.Introduce();
Console.WriteLine($"Department: {Department}");
}
}
new Employee("Bob", 28, "Engineering").Introduce();
Interfaces
interface IDrawable
{
void Draw();
}
interface IResizable
{
void Resize(double factor);
}
class Rectangle : IDrawable, IResizable
{
public double Width { get; set; }
public double Height { get; set; }
public void Draw() => Console.WriteLine($"Drawing {Width}x{Height} rectangle");
public void Resize(double factor)
{
Width *= factor;
Height *= factor;
}
}
Abstract Classes
abstract class Shape
{
public abstract double Area();
public void Describe() => Console.WriteLine($"Area: {Area():F2}");
}
class Circle : Shape
{
public double Radius { get; set; }
public override double Area() => Math.PI * Radius * Radius;
}
Access Modifiers
| Modifier | Scope |
|---|---|
public |
Anywhere |
private |
Same class only |
protected |
Class and derived classes |
internal |
Same assembly |
Use the most restrictive modifier that still allows necessary access.
Sealed Classes and Methods
public sealed class FinalService { } // cannot inherit
public class Base
{
public virtual void Process() { }
}
public class Derived : Base
{
public sealed override void Process() { } // no further override
}
Seal classes not designed for inheritance; seal overrides to prevent further specialization.
Structs vs Classes
public readonly struct Point
{
public double X { get; init; }
public double Y { get; init; }
public double DistanceTo(Point other) =>
Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
}
Use structs for small, immutable value types (coordinates, colors). Use classes for identity-based objects with behavior.
Composition Over Inheritance
public interface ILogger { void Log(string message); }
public class OrderService
{
private readonly ILogger _logger;
public OrderService(ILogger logger) => _logger = logger;
public void PlaceOrder(Order order)
{
_logger.Log($"Order {order.Id} placed");
}
}
Favor injecting behavior over deep inheritance hierarchies.
Polymorphism Example
ILogger logger = new FileLogger("app.log");
logger = new ConsoleLogger(); // swap implementation
logger.Log("Same interface, different behavior");
Common Pitfalls
- Deep inheritance trees — hard to maintain; prefer interfaces and composition.
- Public fields instead of properties — breaks encapsulation and data binding.
- Not calling
basein overridden methods when base behavior is still needed.