SOLID Principles in C# (C sharp), Learn Best OOP Design Practices

February 20, 2024
5 minutes read
By DI Solutions
Blogger Image

SOLID Principles in C# (C sharp), Learn Best OOP Design Practices.

SOLID Principles C sharp One of the most well-known sets of design principles for Object-Oriented Programming (OOP) that help programmers write readable, reusable, maintainable, and scalable code is SOLID. We can handle various software design issues thanks to SOLID design concepts.

These guidelines were created in the 1990s by Robert C. Martin. These principles give us strategies for transitioning from tightly linked code and minimal encapsulation to the desired outcomes of adequately encapsulated and loosely coupled genuine business demands. It stands for the following five design tenets in the mnemonic form:

  • Single Responsibility Principle (SRS)
  • Open/Closed Principle (OCP)
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle (ISP)
  • Dependency Inversion Principle (DIP)

This blog will explore the idea of all Object-Oriented Design principles, including the SOLID principles in C#, and explain why we should apply them to software development. With the help of real-world C# examples, we will examine how to implement these ideas successfully in your application. If you have any OOP knowledge, even if you are not a C# developer, you can still create reliable, manageable software.

Why should we use SOLID design concepts?

We'll go through five principles and provide code examples from our project to help you fully comprehend SOLID concepts and how to successfully use them in your projects. The creation of a web application for order fulfillment is the context here.

  • Shopping and putting items into the basket
  • Place an order
  • Export invoice

First and foremost, we must develop the back-end side API in .NET Core.
Hire Dedicated ASP.NET Developers

We offer top-notch developer hiring services to fulfill your company's needs.

Single Responsibility Principle (SRP)

There needs to be just one changeable factor per class. This concept was provided by Robert C. Martin later reprinted the book titled “Agile Software Development, Principles, Patterns, and Practices” as “Agile Principles, Patterns, and Practices in C#” in the C# (c sharp) version of the book.

At the early stages of an application’s design, the Single Responsibility Principle offers us a useful method for identifying classes and forces us to consider all the potential changes that may occur to a class.

But only after we have a complete understanding of how the application should function can we effectively separate out the roles. The Single Responsibility Principle, one of the SOLID principles in C#, makes it easy to recognize classes throughout the application design stage and forces you to consider all the potential changes a class may undergo.

But only after we have a complete understanding of how the application should function can we effectively separate out the roles.

We decided to develop this project using a 3-layer design to make the coding process easier. Three layers make up our application: API, Application, Entity, and Infrastructure (a.k.a. Data Access Layer).

Project Structure

The graphics above represent each layer’s responsibility in the project structure. API, Application, Data Access Layer, and so on.

Class Structure

This class is intended to handle only one functionality. If a layer has numerous classes, they should all be related to the same responsibility. You can see how service classes are organized in the example.

Method Structure

In this scenario, each method in the Basket class is also responsible for one feature. This method allows you to arrange your code more clearly and make future reworking easier. Consider the operation of a garage service facility. Open gate, closing gate, and performing service are its three primary functions. The example below transgresses the SRP principle.

The code below violates the SRP principle because it combines the primary function of vehicle servicing with open-gate and shut-gate duties. By modifying the aforementioned code and adding an interface, we can appropriately use SRP. Gate-related functions are transferred to a new class named GarageStationUtility, and a new interface called IGarageUtility is developed, adhering to SOLID principles in C#.

Open/Closed Principle (OCP)

According to this approach, the class should be simple to expand, but its fundamental implementations should not need to be altered. The program or application should be adaptable to change. Applying change management significantly influences the effectiveness of a system’s application or software. The Open-Closed Principle (OCP), one of the SOLID principles in C#, states that it is possible to expand the system’s behavior without changing its current implementation.

Consider bank accounts such as normal savings, salary savings, corporate, and so on for various consumers. There are distinct restrictions and interest rates for each client category. If the bank develops a new Account type, the code below violates the OCP principle.

The code in question affects the process of creating a new account type. While extending functionality, we may use OCP by utilizing interfaces, abstract classes, abstract methods, and virtual methods. I’ve merely used an interface here as an example; however, you may change it to suit your needs.

The preceding code produces three additional classes: normal saving account, SalarySavingAccount, and CorporateAccount, by extending the IAccount interface. This overcomes the problem of class modification, and we may increase functionality by extending the interface. Because each class only performs one duty and we are not altering the class, the above code implements both the OCP and SRP principles.

Liskov Substitution Principle (LSP)

According to this concept, if you have a parent class and a child class in your project, the child class can replace the parent class without affecting the application’s validity. In this application, business analysts requested a default exporting form for the invoice in addition to the signature mechanism.

As a result, we created the Export() function in the InvoiceBase class to create the export feature’s default form. Let’s look at the code sample now. We need to rework this code and add an interface with a method called GetShape in order to fix the implementation discussed above.

Interface Segregation Principle (ISP)

We should not require clients to implement interfaces that they don’t utilize. It is preferable to build several small interfaces, each supporting a different submodule with sets of functions, rather than having a single big interface.

Our order fulfillment application accepts two payment options as per the need. Paying using a bank account is the first option, and using an e-wallet is the second. The technique to code is incorrect here. This violation may be fixed by splitting the IOrder Interface.

Read More:- Future of C#: Learn, Use, Flexibility all you need to know 2024

Dependency Inversion Principle (DIP)

According to the Dependency Inversion Principle (DIP), high-level modules/classes should not rely on low-level modules/classes. To begin, both should be based on abstractions. Second, abstractions should not be dependent on specifics. Ultimately, abstractions should provide the foundation for details.

High-level modules/classes are responsible for implementing business rules or logic in a system (application). Low-level modules/classes handle more specific actions, such as writing data to databases or passing messages to the operating system or services. Let’s begin with an illustration.

The DIP has been implemented using IoC and an injection function Object() [native code] in the code below. Dependency injection may be implemented in a variety of ways. I’ve used function Object() [native code] injection in this case, but you may also inject dependencies into public class members like fields, events, index properties, and the function Object() [native code] (function Object() [native code] injection), setters (setter injection), and methods (method injection).

The higher-level module in the code above is AutomobileController, and the IAutomobile interface is in an abstraction layer. We’ve combined everything into a single piece of code here, but in reality, each abstraction layer is a separate class with additional functionality. With the IAutomobile interface, products are totally separated from the consumer. With reference to the interface IAutomobile, the object is injected into the function Object() [native code] of the AutomobileController class. The injection function Object() [native code] is the function Object() [native code] where the object is injected, adhering to SOLID principles in C#.

DI is a software design paradigm that enables the creation of loosely connected programming. We can eliminate tight connectivity between program components by using DI. DI also enables us to better manage future modifications and other challenges in our product. DI’s goal is to make code more sustainable.