Introduction
This is an ongoing project that will be updated regularly, the goal here is to serve as quick reference of some extent of object oriented programing in the C# language.
Object Oriented Programing Fundamentals
The fundaments of OOP such as Classes, Objects, Encapsulation and Hierarchy, will be covered here with examples in code.
Classes
A Class is code structure whos main responsibility is to map an object of a certain domain. Within a class scope one can program behavior through methods. In the C# programing language a class is a reference type.
public class Book
{
string Author { get; set; }
string Name { get; set; }
public double GetPrice()
{
return 100.00;
}
}
Classes are implemented to provide a more organized, reusable and clean code. Especially when you deal with applications with thousands of lines of code and business rules. Classes can be modeled as diagrams such as the Unified Modeling Language (UML), the figure below shows the UML representation of the class Book.
UML Representation of the class Book
Designing Classes
When creating a new class it is important to design objects that are easily maintainable, usable and can be extended. To maintain good object-oriented design practices, principles such as SOLID are recommended to guide developers on the creation and design of classes. These principles may help developers in their journey of creating well designed applications and avoid projects that turns into the big ball of mud. The table below shows brief review of the SOLID principles.
Letter | Meaning | Definition |
---|---|---|
S | Single Responsibility Principle | Classes should have only one responsibility. Example: One class should not be responsible for validating business logic and saving data to the database. |
O | Open/closed principle | The class object should be open for extension and closed for modification. Example: with the use of interface, new objects can be included in the code without having to change the existing code. |
L | Liskov substitution principle | A base class should be replaceable with derived classes at any moment. Example: A dog can walk, however an electric dog can only walk if it has batteries. |
I | Interface segregation principle | An interface should have single responsibility. Example: A person interface should not implement methods regarding animals. |
D | Dependency inversion principle | The use of classes should depend on abstractions(interfaces, base class) and not concrete classes. Example: The use of logging in an application should depend on an interface, because in the future one can change the log framework easily. |
Encapsulation
The use of encapsulation is essential in object-oriented development. The strategy here is to keep implementations hidden in a class and expose only what is necessary. For example the interface of the object should be exposed and not the implementation. C# has resources that can be used to encapsulate a class with the use of access modifiers, interfaces and properties.
The code bellow has an C# code with a Book class. Notice that the author, name and price are private. Private members can only be accessed within the same type. Hiding private elements from inside an object is a strategy for good software development, so that only what is really necessary will be accessible to other objects.
public class Book
{
private readonly string _author;
private readonly string _name;
public double Price { get; private set; }
public Book(string author, string name, double price)
{
_author = author;
_name = name;
Price = price;
}
public string GetAuthor() => _author;
public string GetName() => _name;
}
The book class is immutable, this means that the values of an object cannot be changed, only if a new object is created. The access modifier GetAuthor() is also public, when creating a new object the GetAuthor() will be visible and return the authors name, however the author name can only be set in the act of creating a new class through its constructor. The readonly keyword assumes that the assignment of a value to a field is allowed in the constructor of the class.
Access Modifiers
The table bellow has a list of Access Modifiers in C# with their respective definitions.
Access Modifier | Definition |
---|---|
public | No restricted access, from same assembly or referencing assembly. |
private | Access limited to the containing type (class or struct). |
internal | Access limited to the containing assembly. |
protected | Access limited to the containing class or derived classes. |
protected internal | Access limited to the containing assembly or derived classes (in another assembly). |
private protected | Access limited to the containing assembly, the containing class or derived classes. |
According to the Microsoft Docs, an assembly is a collection of types and resources that are built to work together and form a logical unit of functionality 2. In visual studio an assembly can be viewed as a project.
Class Hierarchy
Inheritance is fundamental to object-oriented development. Just like the family hierarchy represented in figure of a tree above, inheritance happens when one class derives from another class. This relationship is created as an โis aโ relation. An example would be that a dog is a animal and a pug is a dog. This type of implementation allows for better modeling and code reuse.
Interface
Interfaces are essential for object-oriented-programming in C#. An interface has signatures that are public of methods, events, properties and indexes. The code bellow has an example of the book interface previously implemented.
interface IBook
{
string GetAuthor { get; }
string GetName { get; }
double Price { get; }
}
public class Book: IBook
{
private readonly string _author;
private readonly string _name;
public double Price { get; private set; }
public Book(string author, string name, double price)
{
_author = author;
_name = name;
Price = price;
}
public string GetAuthor => _author;
public string GetName => _name;
}
When creating interface a convention in C# is the use of a capital I as the interface prefix. This allows for better representation and identification of interfaces. Since C# 8, it is possible to implement default code for interface members.
By default interface members are public, the available access modifiers for interfaces are: public
, protected
, internal
, private
, protected internal,
, private protected
. The private modifier is required to be implemented by default.
Base and Derived Classes
Base classes can implement code that are reused in derived classes. With this in mind, when working with class Hierarchy one could easily extend or replace the behavior of a base class.
The virtual
and override
access modifiers are used for changing behavior of methods. Implementing virtual methods permits derived classes to override(change) the behavior from their base class. The example below has a BookBase (base class) with changes in the GetInfo method in the Book (derived class).
public class BookBase
{
private readonly string _author;
private readonly string _name;
public double Price { get; private set; }
public BookBase(string author, string name, double price)
{
_author = author;
_name = name;
Price = price;
}
public string GetAuthor => _author;
public string GetName => _name;
protected virtual string GetInfo => $"{_author} {_name}";
}
public class Book : BookBase
{
public Book(string author, string name, double price) : base(author, name, price)
{
}
protected override string GetInfo => $"{GetAuthor} - {GetName} - ${Price}";
}
Top comments (1)
Thanks for the solid breakdown Caio! This was easy to follow along with and had easy to understand examples. Keep up the good work๐๐ป