Core Concepts of Object-Oriented Programming in C#
Inheritance and Class Hierarchies
Inheritance is a fundamental pillar of Object-Oriented Programming (OOP) that allows a class (derived or child class) to acquire the members of another class (base or parent class), with the exception of private members. This mechanism promotes code reusability and establishes a natural hierarchy between types.
When instantiating a derived class, the execution of constructors follows a specific order: the base class constructor executes before the derived class constructor. This ensures that base-level initialization is complete before the derived class logic runs. If the base class lacks a parameterless constructor and requires arguments, the derived class must explicitly pass these parameters using the base keyword.
public class Employee
{
public Employee(string identifier)
{
// Initialization logic
}
}
public class Manager : Employee
{
// The derived class must supply the required argument to the base constructor
public Manager(string id) : base(id)
{
// Manager-specific initialization
}
}
Method Overriding and Hiding
Derived classes can modify the behavior of inherited methods. This is generally achieved through two distinct approaches: method hiding (shadowing) and method overriding.
Method Hiding with new
The new keyword is used to explicitly hide a base class method. The method that gets invoked depends on the compile-time type of the reference variable rather than the runtime instance type.
public class BaseComponent
{
public void Execute()
{
Console.WriteLine("Base execution");
}
}
public class DerivedComponent : BaseComponent
{
public new void Execute()
{
Console.WriteLine("Derived execution");
}
}
Polymorphic Overriding with virtual and override
For true polymorphism, the base class marks the method as virtual, indicating it can be overridden. The derived class then uses the override keyword to provide specific implementation. This ensures that the runtime type of the object determines which method is called.
public class DataProvider
{
public virtual void Connect()
{
Console.WriteLine("Connecting to default source");
}
}
public class SqlProvider : DataProvider
{
public override void Connect()
{
Console.WriteLine("Connecting to SQL Server");
}
}
Abstract Classes and Members
Abstract classes serve as incomplete blueprints for other classes. They cannot be instantiated directly. While an abstract class can contain both abstract and non-abstract members, any method marked as abstract must lack implementation and reside within an abstract class. Derived classes are obligated to implement all abstract members.
This is useful for defining concepts that are too general to exist as concrete objects. For example, a "Shape" is a concept, but specific logic is required for a "Circle" or "Rectangle".
public abstract class Shape
{
// Abstract method has no body
public abstract double CalculateArea();
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double CalculateArea()
{
return Width * Height;
}
}
Interfaces
Unlike class inheritance, which is single, a class can implement multiple interfaces. An interface defines a contract—a collection of public method signatures, properties, or events—without providing the implementation logic. The class implementing the interface is responsible for fulfilling this contract, which facilitates loose coupling and modular design.
public interface ILogger
{
void LogMessage(string message);
}
public class FileLogger : ILogger
{
public void LogMessage(string message)
{
// Implementation details
}
}
File I/O Operations
C# provides robust namespaces for file system manipulation. Commonly used classes include FileStream for handling raw file streams, and StreamWriter or StreamReader for writing and reading text data efficiently. For routine operations like creating, copying, or deleting files, the static methods of the File class offer a simplified API.