Here is an overview of OOP in C#:
OOP or Object-Oriented Programming is a programming paradigm that organizes software design around data, or objects, rather than functions and logic.
C# strongly supports OOP through classes, inheritance, polymorphism, and encapsulation.
Some key OOP concepts in C#:
Classes - Classes form the basic building blocks of OOP. They encapsulate both data and functionality together. A class defines a new data type, with its own properties and methods.
Inheritance - Inheritance allows one class to acquire the properties and behaviors of another class. In C#, a class can inherit from only one base class but can implement multiple interfaces.
Polymorphism - Polymorphism means "many forms" and refers to the ability of a message to be displayed in more than one form. In C#, polymorphism is achieved through method overriding and method overloading.
Encapsulation - Encapsulation binds together the data and the functions that manipulate that data, thereby restricting access to the data. In C#, encapsulation is achieved by declaring class members as private.
Abstraction - Abstraction involves hiding unnecessary details and showing only relevant details. Abstract classes and interfaces provide abstraction in C#.
Classes
Classes are the building blocks of OOP that encapsulate both data and functionality. A class defines a new data type with its own properties and methods. The basic syntax of a class in C# is:
public class ClassName
{
// Fields, properties and methods
}
Properties: Properties are a special kind of member that provides a flexible mechanism to read, write or compute the value of a private field. Properties can be used as if they are public fields, but they can implement different behaviors. The basic syntax for a property is:
public type PropertyName { get; set; }
Methods: Methods are functions defined within a class. They encapsulate a set of statements that perform some specific task. The basic syntax of a method is:
public returnType MethodName(parameter list)
{
// Method body
}
Example:
public class Employee
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
public void Introduce()
{
Console.WriteLine($"My name is {Name}.");
}
}
Here we have a class Employee with:
A private field
name
A public property
Name
to accessname
A public method
Introduce()
Objects
Instances of a class in C# refer to objects created from that class. A class is like a blueprint that defines the properties and behaviors, while an instance is a concrete realization of that blueprint.
To create an instance of a class in C#, you use the new keyword:
Employee employee1 = new Employee();
Employee employee2 = new Employee();
Here we have created two instances of the Employee class - employee1 and employee2.
Each instance has:
Its own copy of the fields defined in the class
Its own set of method calls and properties
Its own lifetime - one instance can be destroyed without affecting the other
For example:
employee1.Name = "John";
employee2.Name = "Jane";
employee1.Introduce();
// My name is John.
employee2.Introduce();
// My name is Jane.
Here we have set different names for the two employee instances. When we call the Introduce() method, each instance prints its own name.
The new keyword does the following:
It allocates memory for the new instance
It initializes the fields of the new instance
It calls the constructor to initialize the new instance
So in summary, instances of a class represent single objects created from that class. They have their own state and lifetime.
Constructor
A constructor in C# is a special method that initializes a newly created object of a class. It has the following characteristics:
It has the same name as the class
It is executed whenever an object of that class is created
It can have multiple signatures for method overloading
It can be overloaded
It is called automatically
The basic syntax of a constructor is:
public ClassName(parameter list)
{
// Constructor body
}
For example:
public class Employee
{
public string Name;
public Employee(string name)
{
Name = name;
}
}
Employee e1 = new Employee("John");
Here we have a constructor that takes a name parameter and initializes the Name field.
When an object is created using the new keyword, the constructor is called to initialize that object.
Constructors are useful for:
Initializing fields to some default values
Performing validation on constructor parameters
Ensuring all required fields are initialized before the object is used.
The benefits of using constructors are:
It ensures that an object is always created in a valid state.
It allows parameterizing the initial state of an object.
Keyword "this"
This keyword in C# refers to the current instance of the class. It has the following uses:
- To refer to the current instance's properties:
public class Employee
{
public string Name;
public Employee(string name)
{
this.Name = name;
}
}
Here we are using this.Name to refer to the Name property of the current instance.
- To call methods on the current instance:
public void Introduce()
{
this.Introduce();
}
Here we are calling the Introduce() method on the current instance using this.
- To pass the current instance as an argument to another method:
public void AssignManager(Employee manager)
{
this.Manager = manager;
}
// Calling method
employee1.AssignManager(this);
Here we are passing the current instance (this) as an argument to the AssignManager() method.
- To disambiguate between local variables and instance variables:
string name;
public Employee(string name)
{
this.name = name; // Instance variable
name = "John"; // Local variable
}
Here we use this.name to refer to the instance variable, and name to refer to the local variable.
So in summary, the this keyword in C# allows you to:
Refer to the current instance's properties and methods
Pass the current instance as an argument
Disambiguate between local variables and instance variables.
It makes the code more self-documenting by making it clear that you are accessing members of the current instance.
Class Variables
Class variables in C# are variables that are declared within a class but outside any method, constructor or block. They are also known as static variables or class fields.
The main characteristics of class variables are:
- They are declared within a class but outside any method.
public class Employee
{
public static string companyName; // Class variable
public Employee(){}
}
They are initialized when the program starts, before any objects are created.
They are shared among all instances of the class. If one instance changes the class variable, it will be changed for all instances.
They can be accessed directly using the class name, without creating an instance.
Employee.companyName = "Acme Inc";
They exist as long as the program is running. Their memory is released when the program ends.
They are declared with the static keyword.
The uses of class variables are:
Storing data that is common to all instances. Like the company name in the example above.
Storing data that needs to be accessible before an instance is created.
The benefits of class variables are:
They reduce memory usage since there is only one copy per class, not per instance.
They allow sharing of data among instances.
Some examples of class variables are:
Constants
Class-level counters
Class-level caches
So in summary, class variables in C# are variables declared within a class but outside any method. They are initialized once and shared among all instances of the class.
Static Classes
A static class in C# is a class that can only contain static members and static constants. It cannot be instantiated.
Some characteristics of static classes are:
They are declared with the static keyword.
They cannot be instantiated (you cannot create an object of a static class).
They can only contain static members like static properties, static methods, static fields etc.
They are used to contain only functionalities that are not dependent on object state.
Example:
public static class MathUtils
{
public static int Max(int a, int b) { ... }
public static double Max(double a, double b) { ... }
}
Here MathUtils is a static class containing only static methods to find the maximum of two numbers.
Static Properties
Static properties in C# are properties that are associated with the class itself rather than with a specific instance of the class.
Some characteristics of static properties are:
They are declared with the static keyword.
They are accessed via the class name, not via a specific instance.
They have one shared copy for all instances of the class.
Example:
public static class Constants
{
public static int MinimumValue = 0;
public static int MaximumValue { get; set; }
}
Here MinimumValue and MaximumValue are static properties of the Constants class.
So in summary, yes C# fully supports:
Static classes which can only contain static members and constants.
Static properties which are associated with the class itself, not a specific instance.
Inheritance
Inheritance in C# allows us to define a class that inherits the properties and behaviors of another class. It allows us to reuse the properties and methods of an existing class when creating a new class.
Some key points about inheritance in C#:
A class inheriting from another is called a derived class, subclass or child class. The class that is inherited from is called the base class, parent class or super class.
The derived class inherits all non-private fields, properties and methods from the base class. It can add its own fields, properties and methods.
The derived class has access to all non-private members of the base class. It can use or override them.
A derived class can specify access modifiers like public, protected etc. But it cannot increase the access of inherited members, only decrease it.
The syntax for inheritance is:
public class DerivedClass : BaseClass
{
// Fields, properties and methods
}
C# supports single inheritance, not multiple inheritance. A class can inherit from only one base class.
C# supports abstract classes and interfaces which can be used instead of multiple inheritance.
Inheritance allows code reusability. The derived class reuses the code of the base class, and adds its own functionality.
Inheritance establishes an "is-a" relationship between the base and derived class. A Dog "is-a" Animal.
Some benefits of inheritance are:
Code reuse: The derived class reuses code from the base class.
Extensibility: The derived class can extend the base class functionality.
Maintainability: If we need to modify the base class, all derived classes are modified.
So in summary, inheritance in C# allows us to define a class that inherits the properties and behaviors of another existing class. The derived class extends the base class and reuses its code.
Polymorphism
Polymorphism in C# refers to the ability of an object to take on many forms. There are two main types of polymorphism in C#:
- Method Overloading: When multiple methods share the same name but differ by type and/or number of parameters. The compiler differentiates them based on the parameters.
Example:
public void Add(int x, int y){}
public void Add(double x, double y) {}
Here the Add()
method is overloaded for int
and double
parameters.
- Method Overriding: When a subclass defines a method with the same name, parameters and return type as a method in its base class. The method in the subclass overrides the method in the base class.
Example:
public class Animal
{
public virtual void MakeSound() {}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Bark! Bark!");
}
}
Here the MakeSound()
method is overridden in the Dog
class.
Benefits of polymorphism:
Loose coupling between classes. The base class does not need to know about the derived class.
Code reusability. The base class code can be reused by derived classes.
Extensibility. New derived classes can be added without modifying existing classes.
In summary, polymorphism allows us to perform a single action in different ways. It gives objects the ability to behave in more than one form. This allows us to abstract implementation details and focus on the interface.
Encapsulation
Encapsulating an object is akin to enclosing valuable assets within a protective shell. While the shell grants access to the contents, it shields the intricate inner workings, much like a watch's hands are visible while its delicate gears remain concealed.
Encapsulation in C# is primarily achieved through the use of classes and access modifiers. Classes provide a blueprint for creating objects, and access modifiers control which parts of a class are accessible from outside the class.
Access Modifiers
C# provides three primary access modifiers:
Public: Public members are accessible from anywhere within the program.
Private: Private members are only accessible within the class they are declared in.
Protected: Protected members are accessible within the class they are declared in and within any derived classes.
By using access modifiers effectively, developers can hide the implementation details of a class, exposing only the necessary methods and properties to interact with the class. This approach promotes data integrity, modularity, and maintainability in software design.
Properties
Properties provide a convenient way to access and modify private fields while maintaining control over the access. They act as intermediaries between the outside world and the private data within a class.
Consider a class representing a bank account:
class BankAccount
{
private double balance;
public double Balance
{
get { return balance; }
set { balance = value; }
}
}
Here, the balance
field is declared as private
, restricting direct access. The Balance
property provides a controlled way to read and modify the balance
field.
Data Hiding
Data hiding is a core principle of encapsulation. By keeping internal data private, developers can ensure that only authorized methods modify the data, preventing unintended changes and maintaining data integrity.
Controlled Access
Encapsulation ensures controlled access to an object's data. By exposing only necessary methods and properties, developers can prevent unauthorized access and misuse of the object's internal state.
Modular Design
Encapsulation promotes modularity by allowing developers to create self-contained units of code. Classes encapsulate data and behavior, making them independent and reusable, enhancing the overall design of software systems.
In conclusion, encapsulation in C# is achieved through the combination of classes, access modifiers, and properties. By effectively utilizing these concepts, developers can design robust, maintainable, and modular software applications.
Use Cases
The .NET Framework provides a huge library of pre-built classes that cover a wide range of functionality, from basic data structures to advanced UI controls.
By using these existing classes in your code, you can:
Save development time: You don't have to re-implement functionality that already exists.
Reduce bugs: The .NET Framework classes are thoroughly tested and production-ready.
Leverage a mature and robust design: The .NET Framework classes have evolved over many years and iterations.
For example, some common .NET Framework classes that you'll likely use in your C# projects are:
String
List
Dictionary<TKey, TValue>
Stream
File
Button
TextBox
And many more...
By creating instances of these classes and calling their methods, you can easily incorporate their functionality into your own code.
For example:
List<string> names = new List<string>();
names.Add("John");
names.Add("Jane");
Here we are using the List class from the .NET Framework.
So in summary, one of the most important uses of classes in C# is to leverage the many robust, production-ready classes available in the .NET Framework. This allows you to build on top of an existing foundation and focus on your specific business logic.
Here are some other important use cases for classes in C#:
Encapsulation: Classes allow you to encapsulate data and functionality into a single unit. The data is hidden from outside code, and can only be accessed/modified through the class's methods. This promotes data hiding and security.
Code organization: Classes help you organize your code logically. You can group related functionality and data into a class. This makes the code easier to understand and maintain.
Inheritance: Classes can inherit from other classes, allowing them to reuse functionality from the parent class. Derived classes can then add or override behavior as needed. This promotes code reuse.
Polymorphism: Classes that inherit from a base class can be used polymorphically. This allows you to write code that is decoupled from specific implementations.
Object orientation: Classes represent real-world objects in your system. They model the properties and behaviors of those objects. This makes the code more intuitive and readable.
Memory management: Classes allow you to create objects on the heap. The .NET Framework handles memory management for these objects.
Namespacing: Classes can be organized into namespaces. This avoids name collisions and further organizes your code.
Templates: Generic classes and methods allow you to write reusable code that works with different data types.
Interfaces: Interfaces define a contract that classes can implement. This decouples implementations from consumers of those implementations.
In summary, classes are one of the fundamental pillars of object-oriented programming in C#. They help you organize your code, encapsulate functionality, reuse code, and model real-world objects in an intuitive way. Their proper use promotes many good software design principles.
Disclaim: I have requested information from Rix AI asking all kinds of questions about classes. As you can see Rix has the information but is difficult to ask for it. If you have other questions be my guest. Try!