OOP ek programming paradigm hai jo objects aur classes ka use karta hai code ko organize aur structure karne ke liye. Isse code ka reuse, maintenance, aur scalability better hoti hai.
Encapsulation ka matlab hai data (attributes) aur methods (functions) jo data pe kaam karte hain, unko ek single unit ya class me bundle karna. Ye kuch components ko restrict karta hai, jisse data ko galti se modify hone se bachaya ja sake.
Example: A class `Person` with private variables and public getter and setter methods to access them.
Inheritance ek class ko dusri class se properties aur methods inherit karne ki permission deta hai, jo code reuse promote karta hai. Jo class inherit karti hai use subclass kehte hain, aur jis class se inherit kiya jaata hai usse superclass kehte hain.
Example: A class `Dog` inherits from the `Animal` class, and thus, it has all properties and methods of `Animal`.
Polymorphism ka matlab hai ki alag-alag classes ke objects ko ek common superclass ke objects ke tarah treat kiya ja sakta hai. Ye methods ko multiple ways me implement karne ki facility deta hai.
Example: A method `makeSound()` could be defined in both `Dog` and `Cat` classes, but the implementation will be different for each.
Abstraction ka concept hai ki complex implementation details ko hide kiya jaaye aur sirf zaruri features hi dikhaye jaaye. Ye abstract classes aur interfaces ke zariye achieve hota hai.
Example: A class `Car` can be abstract, providing a general blueprint for all car types, while specific car types like `ElectricCar` or `SportsCar` implement detailed features.
Object-Oriented Programming (OOP) is a programming paradigm that uses objects and classes to organize and structure code. It allows for better code reusability, maintainability, and scalability.
Encapsulation is the concept of bundling data (attributes) and methods (functions) that operate on the data into a single unit or class. It also restricts access to some of the object's components, which can prevent the accidental modification of data.
Example: A class `Person` with private variables and public getter and setter methods to access them.
Inheritance allows a class to inherit properties and methods from another class, promoting code reuse. The class that inherits is called the subclass, and the class from which it inherits is called the superclass.
Example: A class `Dog` inherits from the `Animal` class, and thus, it has all properties and methods of `Animal`.
Polymorphism allows objects of different classes to be treated as objects of a common superclass. It also allows methods to be implemented in multiple ways.
Example: A method `makeSound()` could be defined in both `Dog` and `Cat` classes, but the implementation will be different for each.
Abstraction is the concept of hiding the complex implementation details and showing only the necessary features of an object. It is achieved using abstract classes and interfaces.
Example: A class `Car` can be abstract, providing a general blueprint for all car types, while specific car types like `ElectricCar` or `SportsCar` implement detailed features.
In Object-Oriented Programming, classes serve as blueprints for creating objects. By dividing the code into different classes, you make the code more modular, easier to maintain, and reusable.
A class can have attributes (properties) and methods (functions) that define the behavior of the objects created from the class.
Once a class is defined, you can create instances (objects) of that class. An object represents an individual instance of the class with its own set of data and behavior.
The process of creating an object is known as instantiation.
class Car {
// Properties
string model;
int year;
// Method
void displayInfo() {
Console.WriteLine("Model: " + model);
Console.WriteLine("Year: " + year);
}
}
class Program {
static void Main() {
// Creating an object of the Car class
Car myCar = new Car();
myCar.model = "Toyota";
myCar.year = 2020;
// Calling method on the object
myCar.displayInfo();
}
}
Explanation: In this simple example, we define a class `Car` with properties `model` and `year`. We create an object `myCar` of the `Car` class, assign values to its properties, and call the method `displayInfo()` to display the car's information.
class Animal {
// Properties
string name;
int age;
// Constructor to initialize properties
public Animal(string n, int a) {
name = n;
age = a;
}
// Method to display animal's details
public void displayDetails() {
Console.WriteLine("Name: " + name);
Console.WriteLine("Age: " + age);
}
}
class Program {
static void Main() {
// Creating objects of the Animal class
Animal dog = new Animal("Buddy", 3);
Animal cat = new Animal("Whiskers", 2);
// Calling method to display details
dog.displayDetails();
cat.displayDetails();
}
}
Explanation: In this example, we define a class `Animal` with a constructor to initialize its properties. We create two objects (`dog` and `cat`) of the `Animal` class, pass values to the constructor, and call the `displayDetails()` method to show their information.
Fields are variables declared inside a class, used to store data. They can have various access modifiers (like public, private, etc.) to control access.
class ClassName {
// Field declaration
accessModifier type fieldName;
}
Example: Here's how you can add fields to a class.
class Person {
// Fields
private string name;
private int age;
}
Explanation: The `Person` class has two private fields: `name` (a string) and `age` (an integer). These fields store the data for an instance of `Person`.
Methods define the behavior of a class. They can take parameters and return values. Methods are defined using the following syntax:
class ClassName {
// Method declaration
accessModifier returnType MethodName(parameters) {
// Method body
}
}
Example: Here's how you can define and use methods in a class.
class Person {
private string name;
private int age;
// Method to display the person's details
public void DisplayDetails() {
Console.WriteLine("Name: " + name);
Console.WriteLine("Age: " + age);
}
// Method to set the person's details
public void SetDetails(string name, int age) {
this.name = name;
this.age = age;
}
}
Explanation: The `Person` class has two methods: `DisplayDetails()` that prints the person's details and `SetDetails()` that assigns values to the `name` and `age` fields.
Properties are members of a class that provide a controlled way to access private fields. They encapsulate data by restricting direct access and allowing only specific ways to read or modify it through get and set accessors.
This ensures data integrity and allows us to add logic when reading or writing data, such as validation or transformation.
A property is defined with a type, a name, and two optional accessors: get (to retrieve the value) and set (to assign a value).
class ClassName {
// Property declaration
public type PropertyName {
get { return field; }
set { field = value; }
}
}
class Person {
private string name; // A private field to store the name.
// Property to get and set the 'name' field.
public string Name {
get { return name; }
set { name = value; }
}
}
// Creating an object of the class and using the property.
Person person = new Person(); // Creating an instance of the 'Person' class.
person.Name = "Alice"; // Using the set accessor to assign a value.
Console.WriteLine(person.Name); // Using the get accessor to retrieve the value.
Explanation: In this example, the Person class has a private field name. The public property Name provides controlled access to this field. - The get accessor is used to retrieve the value of name. - The set accessor is used to assign a value to name, and you can add additional logic here, such as validation.
Object initializer syntax allows you to set property values directly at the time of object creation, making the code concise and more readable. This is especially useful when you need to initialize multiple properties.
class Person
{
private string name;
private int age;
// Property for 'name'
public string Name
{
get { return name; }
set { name = value; }
}
// Property for 'age'
public int Age
{
get { return age; }
set
{
if (value >= 0) // Adding validation for age.
age = value;
else
throw new ArgumentException("Age cannot be negative.");
}
}
}
// Using object initializer syntax
Person person = new Person
{
Name = "Alice", // Setting the 'Name' property.
Age = 22 // Setting the 'Age' property.
};
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
Explanation: In this example, the Person class has properties Name and Age. Instead of setting each property individually after creating the object, the object initializer syntax sets the values directly when the object is created.
This approach improves code clarity and avoids the need for additional constructors, making it easier to initialize objects with multiple properties.
Properties offer several advantages over public fields:
Events are a way for one part of your program to notify other parts when something happens. For example, when you press a button, an event is raised, and other parts of the program react to it (e.g., showing a message or changing the screen).
Events in C# rely on delegates, which are types that define the method signature for event handlers. When an event is raised, all methods (subscribers) attached to that event are executed.
Think of events as a "broadcast" system:
Events are declared using the event keyword. The publisher uses a method to trigger the event when the appropriate condition occurs.
class ClassName {
// Event declaration
public event EventHandler EventName;
// Method to trigger the event
public void TriggerEvent() {
if (EventName != null) {
EventName(this, EventArgs.Empty); // Notify subscribers
}
}
}
Let’s break this down:
using System;
class Button
{
// Event declaration for the button click
public event EventHandler Click;
// Method to simulate a button click
public void PerformClick()
{
Console.WriteLine("Button is clicked.");
TriggerClickEvent(); // Notify all subscribers
}
// Method to trigger the Click event
private void TriggerClickEvent()
{
if (Click != null) // Check if there are subscribers
{
Click(this, EventArgs.Empty); // Notify subscribers
}
}
}
class Program
{
static void Main()
{
// Create a Button object
Button button = new Button();
// Subscribe to the Click event
button.Click += Button_ClickHandler;
// Simulate button click
Console.WriteLine("Simulating a button click...");
button.PerformClick();
}
// Event handler for the button click
static void Button_ClickHandler(object sender, EventArgs e)
{
Console.WriteLine("Button click event handled!");
}
}
Explanation: Here's how this works:
The second parameter of the EventHandler delegate is an EventArgs object. It allows events to send additional data to subscribers. For example, a file download event might include progress information.
However, if no additional data is needed, EventArgs.Empty is used as a placeholder. It’s a predefined static field in the EventArgs class representing an empty event argument.
A constructor is a special method used to initialize objects when they are created. It is called automatically when an object is instantiated from a class. Constructors help set up the initial state of the object.
Constructors do not have a return type (not even void) and must have the same name as the class they belong to.
Syntax:
class ClassName {
// Constructor
public ClassName() {
// Initialization code
}
}
Example: Here's an example of a simple constructor.
class Person {
private string name;
// Constructor to initialize the name field
public Person(string name) {
this.name = name;
}
public void DisplayName() {
Console.WriteLine("Name: " + name);
}
}
Explanation: The `Person` class has a constructor that takes a `string` parameter to initialize the `name` field when a new object is created.
A default constructor is a constructor that does not take any parameters. It initializes an object with default values.
class ClassName {
// Default constructor
public ClassName() {
// Initialization code with default values
}
}
Example: Here's a default constructor.
class Person {
private string name;
// Default constructor
public Person() {
name = "Unknown"; // Default value
}
public void DisplayName() {
Console.WriteLine("Name: " + name);
}
}
Explanation: The `Person` class has a default constructor that sets the `name` to `"Unknown"` if no value is provided during object creation.
A parameterized constructor takes one or more arguments and allows you to initialize an object with specific values at the time of creation.
class ClassName {
// Parameterized constructor
public ClassName(type parameter1, type parameter2) {
// Initialization code with specific values
}
}
Example: Here's a parameterized constructor.
class Person {
private string name;
private int age;
// Parameterized constructor
public Person(string name, int age) {
this.name = name;
this.age = age;
}
public void DisplayInfo() {
Console.WriteLine("Name: " + name);
Console.WriteLine("Age: " + age);
}
}
Explanation: The `Person` class has a parameterized constructor that takes `name` and `age` as parameters to initialize the object.
Constructor overloading refers to having multiple constructors in a class with different parameter lists. This allows you to create objects in different ways depending on the provided arguments.
The constructors must differ in the number or type of parameters, but they must have the same name as the class.
class ClassName {
// Overloaded constructors
public ClassName() {
// Default constructor
}
public ClassName(type parameter) {
// Constructor with one parameter
}
public ClassName(type parameter1, type parameter2) {
// Constructor with two parameters
}
}
Example: Here's an example of constructor overloading.
class Person {
private string name;
private int age;
// Default constructor
public Person() {
name = "Unknown";
age = 0;
}
// Parameterized constructor with name
public Person(string name) {
this.name = name;
age = 0;
}
// Parameterized constructor with name and age
public Person(string name, int age) {
this.name = name;
this.age = age;
}
public void DisplayInfo() {
Console.WriteLine("Name: " + name);
Console.WriteLine("Age: " + age);
}
}
Explanation: The `Person` class demonstrates constructor overloading with three constructors: a default constructor, a constructor that takes only the `name`, and a constructor that takes both `name` and `age`.
In object-oriented programming, access modifiers are used to set the visibility and accessibility of class members (fields, properties, methods, etc.). By using the right access modifier, you can control which parts of your class are accessible from outside the class and ensure that sensitive data is protected while maintaining functionality.
Access modifiers control the visibility and accessibility of class members to other classes or code outside of the class. The main access modifiers in C# are:
Syntax:
class ClassName {
// Access modifier examples
public string publicField;
private int privateField;
protected string protectedField;
internal string internalField;
protected internal string protectedInternalField;
}
Explanation: The above code snippet demonstrates the declaration of class members with different access modifiers.
When a class member is declared as public, it can be accessed from anywhere in the program, both within the class and from outside the class.
class Person {
public string name;
public void DisplayName() {
Console.WriteLine("Name: " + name);
}
}
class Program {
static void Main() {
Person person = new Person();
person.name = "John"; // Accessible because it's public
person.DisplayName();
}
}
Explanation: The `name` field and `DisplayName()` method are declared as `public`, which allows them to be accessed directly from the `Program` class.
When a class member is declared as private, it can only be accessed within the class that defines it. It is not visible or accessible from outside the class.
class Person {
private string name;
public void SetName(string name) {
this.name = name; // Private field accessed via a public method
}
public void DisplayName() {
Console.WriteLine("Name: " + name);
}
}
class Program {
static void Main() {
Person person = new Person();
person.SetName("Alice"); // Accessing private field through a public method
person.DisplayName();
}
}
Explanation: The `name` field is private, so it can only be accessed within the `Person` class. We use the `SetName()` method (which is public) to assign a value to the private field.
The protected modifier allows access to a class member only within the class that defines it and any derived (child) classes.
class Animal {
protected string name;
public Animal(string name) {
this.name = name;
}
}
class Dog : Animal {
public Dog(string name) : base(name) {}
public void DisplayInfo() {
Console.WriteLine("Dog's name: " + name); // Accessing protected field in derived class
}
}
class Program {
static void Main() {
Dog dog = new Dog("Max");
dog.DisplayInfo();
}
}
Explanation: The `name` field is protected, so it can be accessed in the derived `Dog` class but not from outside the class hierarchy.
The internal modifier allows access to a class member only within the same assembly (i.e., project). It is not accessible from other assemblies, even if they reference the current assembly.
class Person {
internal string name;
public void DisplayName() {
Console.WriteLine("Name: " + name);
}
}
class Program {
static void Main() {
Person person = new Person();
person.name = "John"; // Accessible because it's internal within the same assembly
person.DisplayName();
}
}
Explanation: The `name` field is marked as internal, meaning it is accessible within the same project or assembly but not from other projects.
The protected internal modifier combines the features of both protected and internal. It allows access to class members from derived classes or from any class within the same assembly.
class Person {
protected internal string name;
public void DisplayName() {
Console.WriteLine("Name: " + name);
}
}
class Program {
static void Main() {
Person person = new Person();
person.name = "Alice"; // Accessible because it's protected internal within the same assembly
person.DisplayName();
}
}
Explanation: The `name` field is accessible in the `Program` class because it is within the same assembly and the field is marked as `protected internal`.
Garbage collection in C# is an automatic memory management process. The garbage collector (GC) helps in freeing up memory by removing objects that are no longer in use. This eliminates the need for manual memory management and helps in preventing memory leaks and resource exhaustion.
Garbage collection is the process by which C# automatically reclaims memory occupied by objects that are no longer accessible. The garbage collector works by identifying objects that are not referenced by any part of the program and cleaning them up to free memory resources.
Syntax:
GC.Collect(); // Force the garbage collector to run (rarely needed)
Explanation: The GC.Collect() method forces the garbage collector to run, but it is generally not recommended to call it manually. The garbage collector runs automatically in the background when needed.
In C#, memory management is automatically handled by the garbage collector (GC). The GC works in the background to manage the heap (where objects are stored) and ensures that objects no longer in use are removed. It tracks object references and cleans up memory when no references to an object remain.
Key points about GC in C#:
class Example {
public void CreateObject() {
// Object will be eligible for garbage collection once it goes out of scope
Example obj = new Example();
}
}
class Program {
static void Main() {
Example example = new Example();
example.CreateObject(); // The obj object will be eligible for garbage collection after this
}
}
Explanation: In the above example, the obj object will be eligible for garbage collection once it goes out of scope. The GC will automatically remove the object from memory when it is no longer referenced.
To manage memory efficiently, developers should follow best practices to ensure that objects are not unnecessarily holding onto memory resources. C# provides features like Dispose() and the using statement to help manage resources efficiently, particularly for unmanaged resources.
Example of using the Dispose pattern:
class Resource : IDisposable {
private bool disposed = false;
public void Dispose() {
if (!disposed) {
// Free unmanaged resources here
disposed = true;
}
GC.SuppressFinalize(this); // Prevent GC from calling finalizer
}
~Resource() {
Dispose();
}
}
class Program {
static void Main() {
using (Resource resource = new Resource()) {
// Use resource here
} // Dispose will be automatically called when exiting the using block
}
}
Explanation: The Dispose() method is used to release unmanaged resources explicitly. The using statement ensures that the Dispose() method is automatically called when the resource is no longer needed, preventing memory leaks.
Inheritance and polymorphism are core concepts in object-oriented programming (OOP) that promote code reuse and flexibility. Inheritance allows one class to inherit members (fields, properties, methods) from another, while polymorphism enables the same method to behave differently depending on the object it is acting on.
Inheritance allows a class to derive properties and behaviors from another class. The class that inherits is called the derived class, and the class it inherits from is called the base class. This promotes code reuse and allows for creating more specialized classes based on general ones.
Syntax:
class BaseClass {
public void DisplayMessage() {
Console.WriteLine("Message from Base Class");
}
}
class DerivedClass : BaseClass {
// Derived class can use methods and properties from BaseClass
public void AdditionalMethod() {
Console.WriteLine("Additional method in Derived Class");
}
}
Explanation: In this example, DerivedClass inherits the method DisplayMessage() from BaseClass. The derived class can also have additional methods.
Polymorphism allows objects of different types to be treated as objects of a common base type. It enables the same method signature to be used with different implementations depending on the object type. Polymorphism can be achieved through method overriding or method overloading.
Types of Polymorphism:
Example of Method Overloading:
class Calculator {
public int Add(int a, int b) {
return a + b;
}
public double Add(double a, double b) {
return a + b;
}
}
class Program {
static void Main() {
Calculator calc = new Calculator();
Console.WriteLine(calc.Add(2, 3)); // Calls Add(int, int)
Console.WriteLine(calc.Add(2.5, 3.5)); // Calls Add(double, double)
}
}
Explanation: The method Add() is overloaded in the Calculator class to handle both integer and double parameters, providing different implementations depending on the argument types.
Example of Method Overriding:
class Animal {
public virtual void MakeSound() {
Console.WriteLine("Animal sound");
}
}
class Dog : Animal {
public override void MakeSound() {
Console.WriteLine("Bark");
}
}
class Program {
static void Main() {
Animal animal = new Animal();
animal.MakeSound(); // Outputs: Animal sound
Dog dog = new Dog();
dog.MakeSound(); // Outputs: Bark
Animal animalDog = new Dog();
animalDog.MakeSound(); // Outputs: Bark (polymorphism in action)
}
}
Explanation: In this example, the Dog class overrides the MakeSound() method from the Animal class. When called on a Dog object, the overridden method executes. This is an example of polymorphism where the method behavior changes based on the object type, even though the same method name is used.
Method overloading is a feature in C# that allows you to define multiple methods with the same name but different parameter types, number of parameters, or both. This enables the method to perform similar operations on different types of data, increasing flexibility and improving code readability.
Method overloading allows a class to have multiple methods with the same name but different signatures. The method signature is determined by the number and types of parameters. The compiler differentiates between these methods based on the parameters passed during the method call.
Syntax:
class MyClass {
public int Add(int a, int b) {
return a + b;
}
public double Add(double a, double b) {
return a + b;
}
public string Add(string a, string b) {
return a + " " + b;
}
}
Explanation: In this example, the method Add() is overloaded to handle different parameter types: integers, doubles, and strings. Each version of the method performs the same operation (addition), but on different types of data.
Method overloading is particularly useful when you want to perform the same operation on different types of data without having to create separate method names for each data type.
Example:
class Display {
public void Show(int number) {
Console.WriteLine("Integer: " + number);
}
public void Show(string message) {
Console.WriteLine("String: " + message);
}
public void Show(double value) {
Console.WriteLine("Double: " + value);
}
}
class Program {
static void Main() {
Display display = new Display();
display.Show(100); // Calls Show(int)
display.Show("Hello, World!"); // Calls Show(string)
display.Show(3.14); // Calls Show(double)
}
}
Explanation: The Show() method is overloaded to handle three different types of input: integers, strings, and doubles. This way, the same method name is used for different types of data without needing to create separate method names like ShowInt(), ShowString(), etc.
Method overloading is also useful when performing operations that require different numbers of parameters. This allows the same method name to handle cases where the number of parameters may vary.
Example:
class Calculator {
public int Multiply(int a, int b) {
return a * b;
}
public int Multiply(int a, int b, int c) {
return a * b * c;
}
}
class Program {
static void Main() {
Calculator calc = new Calculator();
Console.WriteLine(calc.Multiply(2, 3)); // Calls Multiply(int, int)
Console.WriteLine(calc.Multiply(2, 3, 4)); // Calls Multiply(int, int, int)
}
}
Explanation: The Multiply() method is overloaded to handle multiplication of two or three integers. The correct method is selected based on the number of parameters passed during the method call.
You can combine method overloading with optional parameters to create methods that behave differently based on the parameters passed.
Example:
class Logger {
public void Log(string message) {
Console.WriteLine("Log: " + message);
}
public void Log(string message, int level) {
Console.WriteLine("Log Level " + level + ": " + message);
}
}
class Program {
static void Main() {
Logger logger = new Logger();
logger.Log("System started."); // Calls Log(string)
logger.Log("System error.", 2); // Calls Log(string, int)
}
}
Explanation: The Log() method is overloaded to accept either a single string message or a string message with a log level. This provides flexibility in logging without requiring separate method names for different log levels.
In C#, error handling and exception management are crucial for building reliable applications. Exceptions are runtime errors that disrupt the normal flow of the program. By using the try, catch, and finally blocks, we can catch and handle exceptions gracefully. Additionally, we can create custom exceptions to handle specific error scenarios.
Exceptions are abnormal events that occur during the execution of a program, often due to runtime errors like division by zero, file access issues, or invalid input. These errors can cause a program to crash unless they are caught and handled.
Common exception types:
The throw keyword in C# is used to explicitly throw an exception, signaling that something has gone wrong in the program. You can throw a predefined exception or create a new one.
Syntax:
throw new Exception("An error occurred.");
Example:
class Program {
static void Main() {
try {
int x = 5;
if (x == 5) {
throw new InvalidOperationException("Invalid operation due to x being 5.");
}
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
}
Explanation: In this example, the throw keyword is used to manually throw an InvalidOperationException when the variable x is equal to 5. The exception is caught by the catch block and its message is displayed.
The try block contains strong that might throw an exception. The catch block handles the exception if it occurs. The finally block, if used, will always execute, regardless of whether an exception was thrown or not, making it ideal for cleanup operations.
Syntax:
try {
// Code that may throw an exception
} catch (ExceptionType ex) {
// Code to handle the exception
} finally {
// Cleanup code (optional)
}
Example:
class Program {
static void Main() {
try {
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // This will throw an exception
}
catch (IndexOutOfRangeException ex) {
Console.WriteLine("Error: " + ex.Message);
}
finally {
Console.WriteLine("This will always execute.");
}
}
}
Explanation: The program attempts to access an element of the array that is out of bounds, which throws an IndexOutOfRangeException. The catch block handles the exception, and the finally block executes regardless of whether an exception was thrown or not, printing a cleanup message.
Custom exceptions are user-defined exceptions that allow you to create more specific error messages for your application. Custom exceptions inherit from the base Exception class and can include additional data or functionality.
Syntax:
class MyCustomException : Exception {
public MyCustomException(string message) : base(message) { }
}
Example:
class MyCustomException : Exception {
public MyCustomException(string message) : base(message) { }
}
class Program {
static void Main() {
try {
throw new MyCustomException("This is a custom exception.");
}
catch (MyCustomException ex) {
Console.WriteLine("Caught custom exception: " + ex.Message);
}
}
}
Explanation: In this example, we define a custom exception MyCustomException that inherits from the Exception class. When the exception is thrown, it is caught by the catch block, and the custom message is displayed.
Inheritance in C# simplifies code maintenance by allowing common functionality to be shared across multiple classes. By using base classes and derived subclasses, we can avoid code duplication, improve reusability, and make extending functionality easier. This promotes better organization and reduces the overall complexity of the codebase.
Inheritance allows common functionality to be written once in a base class and inherited by derived classes. This reduces duplication and ensures that changes made to the base class are reflected in all subclasses, making maintenance easier and more efficient.
Example:
class Animal {
public void Eat() {
Console.WriteLine("Animal is eating.");
}
}
class Dog : Animal {
public void Bark() {
Console.WriteLine("Dog is barking.");
}
}
class Program {
static void Main() {
Dog dog = new Dog();
dog.Eat(); // Inherited from Animal class
dog.Bark(); // Specific to Dog class
}
}
Explanation: The Eat() method is defined once in the base class Animal. The derived class Dog inherits this method and adds its own functionality (the Bark() method). If the Eat() method needs to be updated, it only needs to be changed in one place (the Animal class).
A base class is a class that contains common functionality that can be shared by multiple derived classes. The derived classes inherit the members of the base class, either using them as-is or overriding them for custom behavior.
Syntax:
class BaseClass {
public void Display() {
Console.WriteLine("This is the base class.");
}
}
class DerivedClass : BaseClass {
// Can use Display method or override it
}
Example:
class Shape {
public void Draw() {
Console.WriteLine("Drawing a shape.");
}
}
class Circle : Shape {
// Inherits Draw method
}
class Program {
static void Main() {
Circle circle = new Circle();
circle.Draw(); // Inherited from Shape class
}
}
Explanation: The Circle class inherits the Draw() method from the base class Shape. This eliminates the need to redefine the drawing behavior for each specific shape.
With inheritance, we can easily extend the functionality of existing classes. When new features or behavior are needed, we can create new derived classes that inherit the functionality of the base class and add or modify methods without changing the original base class code.
Example:
class Vehicle {
public void Start() {
Console.WriteLine("Vehicle is starting.");
}
}
class Car : Vehicle {
public void Drive() {
Console.WriteLine("Car is driving.");
}
}
class Truck : Vehicle {
public void LoadCargo() {
Console.WriteLine("Truck is loading cargo.");
}
}
class Program {
static void Main() {
Car car = new Car();
car.Start(); // Inherited from Vehicle
car.Drive(); // Specific to Car
Truck truck = new Truck();
truck.Start(); // Inherited from Vehicle
truck.LoadCargo(); // Specific to Truck
}
}
Explanation: Both Car and Truck classes inherit the Start() method from the base class Vehicle. They also extend functionality with their own methods (Drive() for the Car class and LoadCargo() for the Truck class). This enables easy extension of functionality without modifying the base class.
Virtual and abstract methods provide mechanisms for defining methods in base classes that can be overridden or implemented by derived classes. These features help create flexible and extensible code. Virtual methods provide default implementations that can be overridden, while abstract methods must be implemented by any derived class.
A virtual method is defined in a base class and provides a default implementation. Derived classes can override the virtual method to provide their own specific implementation.
Syntax:
class BaseClass {
public virtual void Display() {
Console.WriteLine("Base class display method.");
}
}
class DerivedClass : BaseClass {
public override void Display() {
Console.WriteLine("Derived class display method.");
}
}
Example:
class Animal {
public virtual void Speak() {
Console.WriteLine("Animal speaks.");
}
}
class Dog : Animal {
public override void Speak() {
Console.WriteLine("Dog barks.");
}
}
class Program {
static void Main() {
Animal myAnimal = new Animal();
myAnimal.Speak(); // Output: Animal speaks.
Dog myDog = new Dog();
myDog.Speak(); // Output: Dog barks.
}
}
Explanation: The Speak() method in the Animal class is virtual, meaning it can be overridden in the Dog class. When Speak() is called on a Dog object, it uses the overridden method, not the base class implementation.
Abstract methods are declared in an abstract class and do not provide an implementation. These methods must be implemented by any non-abstract class that inherits from the abstract class. An abstract class cannot be instantiated directly.
Syntax:
abstract class BaseClass {
public abstract void Display();
}
class DerivedClass : BaseClass {
public override void Display() {
Console.WriteLine("Implemented in Derived class.");
}
}
Example:
abstract class Animal {
public abstract void Speak();
}
class Dog : Animal {
public override void Speak() {
Console.WriteLine("Dog barks.");
}
}
class Program {
static void Main() {
Dog myDog = new Dog();
myDog.Speak(); // Output: Dog barks.
}
}
Explanation: The Speak() method in the Animal class is abstract, meaning that any derived class (like Dog) must provide an implementation for this method. The Dog class implements the Speak() method.
1. Implementation: A virtual method provides a default implementation in the base class, while an abstract method does not provide an implementation and must be implemented by derived classes.
2. Override requirement: A virtual method can be optionally overridden in derived classes, whereas an abstract method must be overridden in all non-abstract derived classes.
3. Class type: A class containing a virtual method can be instantiated, whereas a class containing an abstract method must be abstract itself and cannot be instantiated directly.
Method overriding allows a derived class to provide its own implementation of a method that is already defined in its base class. It ensures that the most specific implementation of a method is called, even when using a base class reference. Method overriding is a key feature of polymorphism in object-oriented programming.
Method overriding occurs when a derived class redefines a base class method with the same signature. It allows the derived class to provide a specific behavior that is different from the base class.
Syntax:
class BaseClass {
public virtual void Display() {
Console.WriteLine("Base class Display method.");
}
}
class DerivedClass : BaseClass {
public override void Display() {
Console.WriteLine("Derived class Display method.");
}
}
Example:
class Animal {
public virtual void Speak() {
Console.WriteLine("Animal speaks.");
}
}
class Dog : Animal {
public override void Speak() {
Console.WriteLine("Dog barks.");
}
}
class Program {
static void Main() {
Animal myAnimal = new Animal();
myAnimal.Speak(); // Output: Animal speaks.
Animal myDog = new Dog();
myDog.Speak(); // Output: Dog barks.
}
}
Explanation: The Speak() method in the Animal class is virtual and is overridden in the Dog class. When the myDog object (of type Animal) calls the Speak() method, the overridden version in Dog is executed.
The override keyword explicitly indicates that a method in a derived class is overriding a virtual or abstract method in its base class. It ensures clarity and prevents accidental redefinition of methods that are not intended to be overridden.
Syntax:
class BaseClass {
public virtual void ShowMessage() {
Console.WriteLine("Base class message.");
}
}
class DerivedClass : BaseClass {
public override void ShowMessage() {
Console.WriteLine("Derived class message.");
}
}
Example:
class Vehicle {
public virtual void StartEngine() {
Console.WriteLine("Starting engine in Vehicle.");
}
}
class Car : Vehicle {
public override void StartEngine() {
Console.WriteLine("Starting engine in Car.");
}
}
class Program {
static void Main() {
Vehicle myVehicle = new Car();
myVehicle.StartEngine(); // Output: Starting engine in Car.
}
}
Explanation: The StartEngine() method in Vehicle is overridden in the Car class using the override keyword. When a Car object (referenced as Vehicle) calls the method, the overridden version in Car is executed.
Consider a scenario where you have a base class defining a general behavior, and subclasses that specify the behavior for different types. Method overriding ensures that the correct implementation is called at runtime.
Example:
class Employee {
public virtual void CalculateSalary() {
Console.WriteLine("Calculating salary for general employee.");
}
}
class Manager : Employee {
public override void CalculateSalary() {
Console.WriteLine("Calculating salary for manager with bonuses.");
}
}
class Intern : Employee {
public override void CalculateSalary() {
Console.WriteLine("Calculating stipend for intern.");
}
}
class Program {
static void Main() {
Employee emp1 = new Manager();
emp1.CalculateSalary(); // Output: Calculating salary for manager with bonuses.
Employee emp2 = new Intern();
emp2.CalculateSalary(); // Output: Calculating stipend for intern.
}
}
Explanation: The base class Employee defines a virtual method CalculateSalary(). Subclasses Manager and Intern override this method to provide specific implementations. This enables polymorphism, where the appropriate method is called based on the actual type of the object.