Mastering Object-Oriented Programming in C#: A Guide to Best Practices

Praveen Kumar
4 min readMar 30, 2023

--

Object-oriented programming (OOP) is a popular programming paradigm that has been widely adopted by software developers in recent years. OOP emphasizes the use of objects, which are instances of classes that encapsulate data and behavior. In this article, we will explore the basics of OOP with C# code and discuss some best practices for implementing it.

Classes and Objects

The foundation of OOP is the concept of a class, which is a blueprint for creating objects. A class defines the data and behavior that an object will have. For example, consider a class called “Person” that defines a person’s name and age:

public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

In this example, the “Person” class has two properties: “Name” and “Age”. Properties are like variables that belong to an object and can be accessed and modified using dot notation. To create an object of the “Person” class, we can use the following code:

Person john = new Person();
john.Name = "John Doe";
john.Age = 30;

In this code, we create a new object of the “Person” class called “john” and set its properties to “John Doe” and 30, respectively.

Inheritance

Another key feature of OOP is inheritance, which allows classes to inherit properties and methods from other classes. For example, consider a class called “Employee” that inherits from the “Person” class:

public class Employee : Person
{
public int Salary { get; set; }
}

In this example, the “Employee” class inherits the “Name” and “Age” properties from the “Person” class, and also has a new property called “Salary”. To create an object of the “Employee” class, we can use the following code:

Employee jane = new Employee();
jane.Name = "Jane Smith";
jane.Age = 25;
jane.Salary = 50000;

Polymorphism

Polymorphism is another important concept in OOP that allows objects to be treated as if they are of different types. For example, consider a method called “PrintDetails” that can print the details of a person or an employee:

public void PrintDetails(Person p)
{
Console.WriteLine("Name: " + p.Name);
Console.WriteLine("Age: " + p.Age);
if (p is Employee)
{
Console.WriteLine("Salary: " + ((Employee)p).Salary);
}
}

In this example, the “PrintDetails” method takes a parameter of type “Person”. If the parameter is an instance of the “Employee” class, the method also prints the employee’s salary.

Best Practices for OOP in C#

Here are some best practices for implementing OOP in C#:

  1. Use meaningful class and method names that accurately describe their purpose.
  2. Encapsulate data by using private fields and public properties to control access to the data.
  3. Avoid exposing internal implementation details to users of your classes.
  4. Use inheritance sparingly and only when it makes sense.
  5. Favor composition over inheritance by creating classes that contain instances of other classes instead of inheriting from them.
  6. Use interfaces to define contracts between classes, rather than inheritance.
  7. Use the “override” keyword to override base class methods and ensure that they behave as expected.

Examples of Wrong and Right Methods

Here are some examples of wrong and right ways to implement OOP in C#:

  1. Wrong Method: Exposing internal implementation details
public class BankAccount
{
public int Balance { get; set; }
    public void Deposit(int amount)
{
Balance += amount;
}
public void Withdraw(int amount)
{
Balance -= amount;
}
}

In this example, the “BankAccount” class exposes its internal implementation details by allowing users to access and modify the “Balance” property directly. This violates the principle of encapsulation and can lead to unexpected behavior.

Right Method: Encapsulating data

public class BankAccount
{
private int balance;
    public int Balance 
{
get { return balance; }
set { balance = value; }
}
public void Deposit(int amount)
{
Balance += amount;
}
public void Withdraw(int amount)
{
Balance -= amount;
}
}

In this example, the “BankAccount” class encapsulates its data by using a private field for the balance and exposing it through a public property with get and set accessors. This ensures that the balance can only be modified through the “Deposit” and “Withdraw” methods, which provide a consistent and safe way to manipulate the data.

2 . Wrong Method: Using inheritance for code reuse

public class Animal
{
public void Eat()
{
Console.WriteLine("Animal is eating...");
}
}
public class Dog : Animal
{
public void Bark()
{
Console.WriteLine("Dog is barking...");
}
}

In this example, the “Dog” class inherits the “Eat” method from the “Animal” class to reuse code. However, this creates a tight coupling between the two classes and can lead to issues if the “Eat” method needs to be changed in the future.

Right Method: Using composition over inheritance

public class Animal
{
public void Eat()
{
Console.WriteLine("Animal is eating...");
}
}
public class Dog
{
private Animal animal = new Animal();
public void Bark()
{
Console.WriteLine("Dog is barking...");
}
public void Eat()
{
animal.Eat();
}
}

In this example, the “Dog” class uses composition to contain an instance of the “Animal” class instead of inheriting from it. This decouples the two classes and provides more flexibility in case the “Eat” method needs to be changed in the future.

Conclusion

OOP is a powerful paradigm that can help developers create more maintainable and scalable code. By using classes, inheritance, and polymorphism, developers can create flexible and extensible software that can adapt to changing requirements. By following best practices, such as encapsulating data and favoring composition over inheritance, developers can ensure that their code is robust and maintainable in the long term.

--

--