0%
Exam Sidemann Logo Light Mode

OBJECT ORIENTED PROGRAMMING

Learning Outcome 2: Applying OOP Principles (Classes, Objects, Encapsulation, Inheritance, Polymorphism)

Want to listen to a tutor?

LEARNING OUTCOME 2

APPLY OBJECT-ORIENTED PROGRAMMING PRINCIPLES TO SOLVE PROGRAMMING PROBLEMS AND WRITE EFFICIENT CODE

CLASS VS. OBJECT

In C#, classes and objects are fundamental concepts for building applications. Here's a breakdown to clarify the distinction:

CLASS:

  • A class is like a blueprint or template that defines the properties (data) and methods (functions) that objects of that class will have. It acts as a reusable specification for creating objects.
  • Think of a class like a cookie cutter for making cookies. The cookie cutter defines the general shape and features of the cookie, but it's not the actual cookie itself.

OBJECT:

  • An object is an instance of a class. It's a concrete entity that holds data specific to that object and can execute the methods defined in the class. You can create multiple objects from a single class, just like you can make many cookies from the same cookie cutter.
  • Each object has its own copy of the properties defined in the class, with values specific to that object.

Here's a table summarizing the key differences:

Feature Class Object
Represents Blueprint or template Concrete instance
Reusability Reusable Not reusable (multiple objects can be created from a class)
Properties Defines properties (data) Holds values for those properties
Methods Defines methods (functions) Can execute those methods

Example:

C#
public class Car // Class definition (blueprint)
{
  public string Model { get; set; } // Property (data)
  public int Year { get; set; } // Property (data)

  public void StartEngine() // Method (function)
  {
    Console.WriteLine("Engine started!");
  }
}

// ... in another part of the code (e.g., Main method) ...
Car myCar = new Car(); // Creating an object of the Car class
myCar.Model = "Tesla Model S"; // Assigning values to object properties
myCar.Year = 2024;
myCar.StartEngine(); // Calling a method on the object

In this example, the Car class defines the blueprint for car objects. We can then create objects (instances) like myCar from this class. Each Car object will have its own Model and Year properties, along with the ability to call the StartEngine() method.

CONSTRUCTORS AND DESTRUCTORS IN C#

CONSTRUCTOR:

  • A constructor is a special method in a class that is called automatically when you create an object of that class (using the `new` keyword). Its primary purpose is to initialize the object's state (its fields and properties) with appropriate starting values.
  • Constructors have the same name as the class and do not have a return type (not even `void`).
  • Think of a constructor as the setup instructions executed when a new object comes into existence.

Example:

C#
public class Car
{
  public string Model { get; set; }
  public int Year { get; set; }

  // Constructor with parameters
  public Car(string model, int year)
  {
    Model = model; // Initialize Model property
    Year = year;   // Initialize Year property
    Console.WriteLine($"A {Year} {Model} was created!");
  }
}

// ... later in the code ...
Car myCar = new Car("Ford Mustang", 2020); // Constructor is called here

In this example, the Car class has a constructor that takes two arguments. When we create myCar using new Car(...), this constructor is executed, setting the Model and Year and printing a message.

DESTRUCTOR:

  • A destructor (using the tilde `~` followed by the class name) is a special method used to perform cleanup operations before an object is finally removed from memory by the Garbage Collector (GC).
  • Its main use case is to release **unmanaged resources** (like operating system file handles, database connections, network sockets, or memory allocated outside the .NET runtime) that the object might hold.
  • Destructors cannot be called explicitly, have no parameters, and no access modifiers. Their execution timing is non-deterministic, controlled by the GC.

Important Note: For cleanup of *managed* resources or for more deterministic cleanup, implementing the IDisposable interface and using the using statement is the preferred and recommended pattern in C# over relying solely on destructors.

ACCESS SPECIFIERS

In C#, access specifiers (or access modifiers) are keywords used to define the visibility or accessibility level of types (like classes, structs, interfaces) and their members (fields, properties, methods). They control which parts of your code can access these elements.

  1. public:

    The type or member can be accessed by any code in the same assembly or another assembly that references it. This is the most permissive level.

    Example:

    C#
    public class MyClass
    {
      public string PublicData = "Accessible Everywhere";
    }
  2. private:

    The type or member can only be accessed by code within the same class or struct that declares it. This is the most restrictive level and the default for members if no modifier is specified.

    Example:

    C#
    public class MyClass
    {
      private int _secretCounter = 0; // Only accessible inside MyClass
    
      public void IncrementCounter()
      {
        _secretCounter++; // OK, accessed from within the class
      }
    }
  3. protected:

    The type or member can be accessed only by code in the same class, or in a class that is derived (inherits) from that class.

    Example:

    C#
    public class BaseClass
    {
      protected string FamilySecret = "Shared within family";
    }
    
    public class DerivedClass : BaseClass
    {
      public void RevealSecret()
      {
        Console.WriteLine(FamilySecret); // OK, accessed from derived class
      }
    }
  4. internal:

    The type or member can be accessed by any code within the same assembly (the project/DLL where they are defined). This is the default accessibility for top-level types (like classes) if none is specified.

    C#
    // In MyLibrary.dll
    internal class HelperUtility
    {
      // ... utility methods ...
    }

    HelperUtility can be used by any other class within MyLibrary.dll, but not by code in a different project referencing MyLibrary.dll.

  5. protected internal:

    The type or member can be accessed by any code in the assembly in which it's declared, OR from within a derived class in another assembly. It's a union of protected and internal access.

  6. private protected:

    The type or member can be accessed only within its declaring assembly, by code in the same class or in a type that is derived from that class. This is the most restrictive combination.

ENCAPSULATION, INHERITANCE, AND POLYMORPHISM

ENCAPSULATION:

  • Concept: Bundling data (attributes/fields/properties) and the methods (functions/behavior) that operate on that data together within a single unit, the class. A key part is often **data hiding**, restricting direct access to some of the object's components.
  • Analogy: Think of a car. You interact with it through pedals, steering wheel, and ignition (public methods/interface), but the complex engine workings (private data and methods) are hidden away and managed internally.
  • Implementation in C#: Using access modifiers like `private` for data fields and providing controlled access through `public` properties (getters/setters) or methods.

BENEFITS OF ENCAPSULATION:

  • Control & Integrity: Prevents outside code from putting the object into an invalid or inconsistent state. The class controls how its data is modified.
  • Simplicity: Hides internal complexity from the user of the class. They only need to know the public interface.
  • Maintainability & Flexibility: Allows the internal implementation of a class to be changed without affecting the code that uses it, as long as the public interface remains stable.

INHERITANCE:

  • Concept: A mechanism where one class (the derived or child class) acquires the properties and methods of another class (the base or parent class). It represents an "is-a" relationship (e.g., a `Dog` is an `Animal`).
  • Analogy: A `Square` "is-a" type of `Shape`. It inherits general shape properties (like position) but adds its own specific properties (side length) or behavior.
  • Implementation in C#: Using the colon (`:`) after the derived class name, followed by the base class name (e.g., `public class Dog : Animal`).

TYPES OF INHERITANCE (in C# context):

  • Single Class Inheritance: A class can directly inherit from only one base class.
  • Multiple Interface Implementation: A class can implement multiple interfaces, inheriting their contracts (method signatures, properties) but not their implementation (unless default interface methods are used).
  • Multilevel Inheritance: Class C inherits from B, which inherits from A.
  • Hierarchical Inheritance: Multiple classes (B, C) inherit from a single base class (A).

BENEFITS OF INHERITANCE:

  • Code Reusability: Avoids duplicating code by defining common attributes and behaviors in a base class.
  • Extensibility: Easily create new classes that build upon existing ones.
  • Polymorphism Foundation: Inheritance is essential for achieving runtime polymorphism through method overriding.

POLYMORPHISM:

  • Concept: Literally means "many forms." In OOP, it's the ability of objects of different classes (related by inheritance) to respond to the same method call in their own unique way. It allows you to treat objects of derived classes as objects of their base class type.
  • Analogy: Different animals (`Dog`, `Cat`) might all respond to a `MakeSound()` command, but each produces its specific sound ("Woof!", "Meow!"). You can tell any `Animal` to `MakeSound()`, and it will do so appropriately based on what kind of animal it actually is.

TYPES OF POLYMORPHISM IN C#:

  • Static (Compile-Time) Polymorphism: Resolved by the compiler before the program runs.
    • Method Overloading: Same method name, different parameter lists within the same class.
    • Operator Overloading: Custom behavior for operators (+, -, etc.) for a class.
  • Dynamic (Runtime) Polymorphism: Resolved when the program is running, based on the actual object type.
    • Method Overriding: A derived class provides a specific implementation for a `virtual` or `abstract` method inherited from its base class, using the `override` keyword.

BENEFITS OF POLYMORPHISM:

  • Flexibility & Extensibility: Allows you to add new derived classes without changing the code that uses the base class references.
  • Simpler Code: You can write code that operates on base class types, and it will automatically work correctly with any derived type objects.

Example Combining Principles:

C#
using System;
using System.Collections.Generic;

// Base class (Encapsulation: private fields, public properties/methods)
public abstract class Shape
{
    // Encapsulated data (can only be accessed via property)
    private string _color;

    public string Color
    {
        get { return _color; }
        protected set { _color = value; } // Protected set allows derived classes to set color
    }

    // Constructor
    public Shape(string color)
    {
        this.Color = color;
    }

    // Abstract method - must be implemented by derived classes (Polymorphism)
    public abstract double GetArea();

    // Virtual method - can be optionally overridden (Polymorphism)
    public virtual void DisplayInfo()
    {
        Console.WriteLine($"Color: {Color}");
    }
}

// Derived class (Inheritance)
public class Circle : Shape
{
    public double Radius { get; private set; } // Encapsulated property

    // Constructor calls base constructor
    public Circle(string color, double radius) : base(color)
    {
        Radius = radius;
    }

    // Override abstract method (Polymorphism)
    public override double GetArea()
    {
        return Math.PI * Radius * Radius;
    }

    // Override virtual method (Polymorphism)
    public override void DisplayInfo()
    {
        base.DisplayInfo(); // Call base method
        Console.WriteLine($"Type: Circle, Radius: {Radius}");
    }
}

// Another derived class (Inheritance)
public class Rectangle : Shape
{
    public double Width { get; private set; }
    public double Height { get; private set; }

    public Rectangle(string color, double width, double height) : base(color)
    {
        Width = width;
        Height = height;
    }

    public override double GetArea()
    {
        return Width * Height;
    }

    public override void DisplayInfo()
    {
        base.DisplayInfo();
        Console.WriteLine($"Type: Rectangle, Width: {Width}, Height: {Height}");
    }
}

public class Program
{
    static void Main(string[] args)
    {
        List<Shape> shapes = new List<Shape>();
        shapes.Add(new Circle("Red", 5.0));
        shapes.Add(new Rectangle("Blue", 4.0, 6.0));
        shapes.Add(new Circle("Green", 2.5));

        Console.WriteLine("--- Shape Details ---");
        foreach (Shape shape in shapes)
        {
            shape.DisplayInfo(); // Polymorphic call to DisplayInfo
            Console.WriteLine($"Area: {shape.GetArea():F2}"); // Polymorphic call to GetArea
            Console.WriteLine("--------------------");
        }
    }
}

Explanation:

  1. Encapsulation: The `Shape` class hides its `_color` field, providing controlled access via the `Color` property. `Circle` and `Rectangle` also encapsulate their specific dimensions (`Radius`, `Width`, `Height`) using auto-properties with `private set` (allowing them to be set only within the class, typically in the constructor).
  2. Inheritance: `Circle` and `Rectangle` inherit from the `Shape` base class using the `: Shape` syntax. They automatically gain the `Color` property and the requirement to implement `GetArea()`. They also inherit the `DisplayInfo()` method.
  3. Polymorphism:
    • `GetArea()` is `abstract` in `Shape`, forcing derived classes to provide their own implementation (method overriding).
    • `DisplayInfo()` is `virtual` in `Shape`, allowing derived classes to optionally `override` it to add more specific details, while still being able to call the base implementation using `base.DisplayInfo()`.
    • In `Main`, a `List` holds different actual types (`Circle`, `Rectangle`). When iterating through the list, calling `shape.DisplayInfo()` and `shape.GetArea()` invokes the correct overridden method for each object at runtime. This is dynamic/runtime polymorphism.

This enhanced example showcases how abstract methods enforce polymorphism and how virtual methods allow optional extension while still leveraging base class functionality, alongside encapsulation and inheritance.

End of Outcome 2 Assessment

1 of 20

Question text will load here.

    Assessment Results

    Score: 0%

    Answered: 0 of 0

    Correct: 0

    Review Incorrect/Skipped Answers: