#C# Classes and Objects
This chapter will detail classes and objects in C#, the foundation of object-oriented programming, including class definition, object creation, constructors, destructors, and related concepts.
#Object-Oriented Programming Basics
#What is a Class?
A class is a blueprint or template for creating objects. It defines the properties (attributes) and behaviors (methods) that objects of that type will have.
#What is an Object?
An object is an instance of a class. It's a concrete entity that has actual values for its properties and can perform actions through its methods.
#Class Definition
#Basic Class Structure
public class Person
{
// Fields (attributes)
public string Name;
public int Age;
public string Email;
// Constructor
public Person(string name, int age, string email)
{
Name = name;
Age = age;
Email = email;
}
// Methods (behaviors)
public void Introduce()
{
Console.WriteLine($"Hi, I'm {Name}, {Age} years old.");
}
public void SendEmail(string message)
{
Console.WriteLine($"Sending email to {Email}: {message}");
}
}#Access Modifiers
public class AccessExample
{
// Public: accessible from anywhere
public string PublicField = "Public";
// Private: accessible only within this class
private string PrivateField = "Private";
// Protected: accessible within this class and derived classes
protected string ProtectedField = "Protected";
// Internal: accessible within the same assembly
internal string InternalField = "Internal";
// Protected Internal: accessible within same assembly and derived classes
protected internal string ProtectedInternalField = "Protected Internal";
// Property to control access
public string ReadOnlyProperty { get; private set; }
// Method with different access levels
public void PublicMethod() { }
private void PrivateMethod() { }
protected void ProtectedMethod() { }
internal void InternalMethod() { }
}#Constructors
#Default Constructor
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Major { get; set; }
// Default constructor
public Student()
{
Name = "Unknown";
Age = 0;
Major = "Undeclared";
}
public void DisplayInfo()
{
Console.WriteLine($"Student: {Name}, Age: {Age}, Major: {Major}");
}
}#Parameterized Constructors
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
// Parameterized constructor
public Product(int id, string name, decimal price, string category)
{
Id = id;
Name = name;
Price = price;
Category = category;
}
// Constructor overloading
public Product(int id, string name, decimal price)
: this(id, name, price, "General")
{
}
public void DisplayInfo()
{
Console.WriteLine($"Product: {Name} (${Price}) - {Category}");
}
}#Constructor Chaining
public class Employee
{
public string Name { get; set; }
public int Age { get; set; }
public string Department { get; set; }
public decimal Salary { get; set; }
// Main constructor
public Employee(string name, int age, string department, decimal salary)
{
Name = name;
Age = age;
Department = department;
Salary = salary;
Console.WriteLine("Employee created with all parameters");
}
// Constructor chaining
public Employee(string name, int age, string department)
: this(name, age, department, 50000m)
{
Console.WriteLine("Employee created with default salary");
}
public Employee(string name, int age)
: this(name, age, "General", 40000m)
{
Console.WriteLine("Employee created with default department and salary");
}
}#Properties
#Auto-Implemented Properties
public class Person
{
// Auto-implemented properties
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
// Computed property
public string FullName
{
get { return $"{FirstName} {LastName}"; }
}
// Read-only property
public bool IsAdult
{
get { return Age >= 18; }
}
}#Properties with Validation
public class BankAccount
{
private decimal _balance;
public decimal Balance
{
get { return _balance; }
set
{
if (value < 0)
throw new ArgumentException("Balance cannot be negative");
_balance = value;
}
}
private string _accountNumber;
public string AccountNumber
{
get { return _accountNumber; }
private set { _accountNumber = value; }
}
public BankAccount(string accountNumber, decimal initialBalance)
{
AccountNumber = accountNumber;
Balance = initialBalance;
}
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Deposit amount must be positive");
Balance += amount;
}
public bool Withdraw(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Withdrawal amount must be positive");
if (Balance >= amount)
{
Balance -= amount;
return true;
}
return false;
}
}#Static Members
#Static Fields and Properties
public class Counter
{
// Static field - shared by all instances
private static int _instanceCount = 0;
// Static property
public static int InstanceCount
{
get { return _instanceCount; }
}
// Instance property
public int Id { get; private set; }
public Counter()
{
_instanceCount++;
Id = _instanceCount;
}
// Static method
public static void ResetCounter()
{
_instanceCount = 0;
}
public void DisplayInfo()
{
Console.WriteLine($"Instance {Id} of {InstanceCount} total instances");
}
}#Static Classes
public static class MathHelper
{
public const double PI = 3.14159;
// Static methods - no instance needed
public static double CalculateCircleArea(double radius)
{
return PI * radius * radius;
}
public static int Max(int a, int b)
{
return a > b ? a : b;
}
public static bool IsEven(int number)
{
return number % 2 == 0;
}
// Static constructor
static MathHelper()
{
Console.WriteLine("MathHelper class initialized");
}
}
// Usage
double area = MathHelper.CalculateCircleArea(5.0);
int max = MathHelper.Max(10, 20);
bool isEven = MathHelper.IsEven(4);#Methods in Classes
#Instance Methods
public class Calculator
{
private double _result;
public double Add(double value)
{
_result += value;
return _result;
}
public double Subtract(double value)
{
_result -= value;
return _result;
}
public double Multiply(double value)
{
_result *= value;
return _result;
}
public double Divide(double value)
{
if (value == 0)
throw new DivideByZeroException("Cannot divide by zero");
_result /= value;
return _result;
}
public double GetResult()
{
return _result;
}
public void Clear()
{
_result = 0;
}
}#Method Overloading
public class Logger
{
public void Log(string message)
{
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}");
}
public void Log(string message, LogLevel level)
{
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}");
}
public void Log(string message, LogLevel level, string category)
{
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] [{category}] {message}");
}
public void Log(Exception exception)
{
Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [ERROR] {exception.Message}");
Console.WriteLine($"Stack Trace: {exception.StackTrace}");
}
}
public enum LogLevel
{
INFO, WARNING, ERROR, DEBUG
}#Object Creation and Usage
#Creating Objects
// Create objects using constructors
Person person1 = new Person(); // Default constructor
Person person2 = new Person("Alice", 25, "alice@example.com"); // Parameterized constructor
Product product1 = new Product(1, "Laptop", 999.99m, "Electronics");
Product product2 = new Product(2, "Mouse", 29.99m); // Uses constructor chaining
// Create objects using object initializer
Person person3 = new Person
{
Name = "Bob",
Age = 30,
Email = "bob@example.com"
};
Product product3 = new Product
{
Id = 3,
Name = "Keyboard",
Price = 79.99m,
Category = "Electronics"
};#Using Objects
// Use object methods and properties
person2.Introduce();
person2.SendEmail("Welcome to our team!");
product1.DisplayInfo();
product2.DisplayInfo();
// Access properties
Console.WriteLine($"Person name: {person3.Name}");
Console.WriteLine($"Person is adult: {person3.IsAdult}");
// Modify properties
person3.Age = 31;
Console.WriteLine($"Updated age: {person3.Age}");#Inheritance Basics
#Base and Derived Classes
// Base class
public class Vehicle
{
public string Brand { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public Vehicle(string brand, string model, int year)
{
Brand = brand;
Model = model;
Year = year;
}
public virtual void Start()
{
Console.WriteLine($"{Brand} {Model} starting...");
}
public virtual void Stop()
{
Console.WriteLine($"{Brand} {Model} stopping...");
}
}
// Derived class
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
public string FuelType { get; set; }
public Car(string brand, string model, int year, int doors, string fuel)
: base(brand, model, year)
{
NumberOfDoors = doors;
FuelType = fuel;
}
public override void Start()
{
Console.WriteLine($"Car {Brand} {Model} ({FuelType}) starting with key...");
base.Start();
}
public void OpenTrunk()
{
Console.WriteLine("Opening car trunk...");
}
}#this Keyword
#this Keyword Usage
public class Student
{
public string Name { get; private set; }
public int Age { get; private set; }
public double GPA { get; private set; }
public Student(string name, int age, double gpa)
{
// Distinguish between parameter and field
this.Name = name;
this.Age = age;
this.GPA = gpa;
}
// Return current object
public Student Clone()
{
return new Student(this.Name, this.Age, this.GPA);
}
// Pass current object to another method
public void PrintInfo()
{
Printer.PrintStudentInfo(this);
}
}
public static class Printer
{
public static void PrintStudentInfo(Student student)
{
Console.WriteLine($"Student: {student.Name}, Age: {student.Age}, GPA: {student.GPA}");
}
}#Object Lifecycle
#Constructor Execution Order
public class LifecycleDemo
{
public static int InstanceCount { get; private set; }
public LifecycleDemo()
{
InstanceCount++;
Console.WriteLine("Instance constructor called");
}
static LifecycleDemo()
{
Console.WriteLine("Static constructor called");
}
~LifecycleDemo()
{
InstanceCount--;
Console.WriteLine("Destructor called");
}
}#Practical Examples
#Bank Account System
public class BankAccount
{
private static decimal _totalDeposits = 0;
private static int _accountCount = 0;
public string AccountNumber { get; }
public string OwnerName { get; }
public decimal Balance { get; private set; }
public DateTime CreatedDate { get; }
public static decimal TotalDeposits => _totalDeposits;
public static int AccountCount => _accountCount;
public BankAccount(string accountNumber, string ownerName, decimal initialDeposit)
{
AccountNumber = accountNumber;
OwnerName = ownerName;
Balance = initialDeposit;
CreatedDate = DateTime.Now;
_accountCount++;
_totalDeposits += initialDeposit;
}
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Deposit amount must be positive");
Balance += amount;
_totalDeposits += amount;
Console.WriteLine($"Deposited ${amount} to account {AccountNumber}");
}
public bool Withdraw(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Withdrawal amount must be positive");
if (Balance < amount)
{
Console.WriteLine($"Insufficient funds in account {AccountNumber}");
return false;
}
Balance -= amount;
Console.WriteLine($"Withdrew ${amount} from account {AccountNumber}");
return true;
}
public void DisplayBalance()
{
Console.WriteLine($"Account {AccountNumber} Balance: ${Balance:N2}");
}
public static void DisplayBankStatistics()
{
Console.WriteLine($"Total Accounts: {AccountCount}");
Console.WriteLine($"Total Deposits: ${TotalDeposits:N2}");
}
}#Product Catalog
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
public int StockQuantity { get; set; }
public bool IsInStock => StockQuantity > 0;
public Product(int id, string name, decimal price, string category, int stock)
{
Id = id;
Name = name;
Price = price;
Category = category;
StockQuantity = stock;
}
public void ApplyDiscount(decimal percentage)
{
if (percentage < 0 || percentage > 100)
throw new ArgumentException("Invalid discount percentage");
Price *= (1 - percentage / 100);
}
public bool Sell(int quantity)
{
if (quantity <= 0)
throw new ArgumentException("Quantity must be positive");
if (StockQuantity < quantity)
{
Console.WriteLine($"Insufficient stock for {Name}. Available: {StockQuantity}, Requested: {quantity}");
return false;
}
StockQuantity -= quantity;
Console.WriteLine($"Sold {quantity} units of {Name}. Remaining stock: {StockQuantity}");
return true;
}
public void Restock(int quantity)
{
if (quantity <= 0)
throw new ArgumentException("Restock quantity must be positive");
StockQuantity += quantity;
Console.WriteLine($"Restocked {quantity} units of {Name}. New stock: {StockQuantity}");
}
public void DisplayInfo()
{
Console.WriteLine($"Product: {Name} (ID: {Id})");
Console.WriteLine($"Category: {Category}");
Console.WriteLine($"Price: ${Price:N2}");
Console.WriteLine($"Stock: {StockQuantity} units");
Console.WriteLine($"Status: {(IsInStock ? "In Stock" : "Out of Stock")}");
}
}
public class ProductCatalog
{
private List<Product> _products = new List<Product>();
public void AddProduct(Product product)
{
_products.Add(product);
}
public Product FindProduct(int id)
{
return _products.FirstOrDefault(p => p.Id == id);
}
public List<Product> GetProductsByCategory(string category)
{
return _products.Where(p => p.Category.Equals(category, StringComparison.OrdinalIgnoreCase)).ToList();
}
public List<Product> GetInStockProducts()
{
return _products.Where(p => p.IsInStock).ToList();
}
public void DisplayCatalog()
{
Console.WriteLine("=== Product Catalog ===");
foreach (var product in _products)
{
product.DisplayInfo();
Console.WriteLine();
}
}
public decimal CalculateTotalInventoryValue()
{
return _products.Sum(p => p.Price * p.StockQuantity);
}
}#Best Practices
#Class Design Principles
// Single Responsibility Principle
public class UserValidator
{
// Only validates user data
public bool IsValidEmail(string email) { /* validation logic */ }
public bool IsValidAge(int age) { /* validation logic */ }
}
public class UserRepository
{
// Only handles user data persistence
public void Save(User user) { /* save logic */ }
public User Load(int id) { /* load logic */ }
}#Encapsulation
public class EncapsulatedExample
{
private int _age;
public int Age
{
get { return _age; }
set
{
if (value < 0 || value > 150)
throw new ArgumentException("Age must be between 0 and 150");
_age = value;
}
}
// Provide methods for business logic
public bool IsAdult() => Age >= 18;
public bool IsSenior() => Age >= 65;
}#Constructor Guidelines
public class GoodConstructor
{
public string Name { get; }
public DateTime CreatedAt { get; }
// Initialize all required fields
public GoodConstructor(string name)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
CreatedAt = DateTime.Now;
}
// Provide factory methods for common scenarios
public static GoodConstructor CreateDefault()
{
return new GoodConstructor("Default User");
}
public static GoodConstructor CreateWithTimestamp(string name, DateTime timestamp)
{
return new GoodConstructor(name) { CreatedAt = timestamp };
}
}#Summary
In this chapter, you learned:
- Class and object concepts
- Access modifiers and their usage
- Constructors: default, parameterized, chaining
- Properties: auto-implemented, with validation
- Static members: fields, properties, methods, classes
- Method overloading in classes
- this keyword usage
- Object lifecycle management
- Practical examples and best practices
Classes and objects are fundamental to object-oriented programming in C#. They provide the structure for organizing code into reusable, maintainable components. In the next chapter, we'll explore encapsulation and properties in more detail.