Object-oriented programming (OOP) is a paradigm (a sort of “style”) of programming that revolves around objects communicating with each other, as opposed to functions operating on data structures. C# is the flagship language of the .NET ecosystem. Despite being a multi-paradigm language, its forte is certainly OOP.
OOP is a recognized programming paradigm, but programming languages differ in how they interpret and implement its tenants. In this post, we’ll offer you a guide on OOP concepts using C# as the language for the examples.
What you’ll get, then, is OOP through C#-colored lenses. That won’t diminish your experience in any way at all for two reasons. First, C# is one of the most popular languages wide there, and learning it is an asset for your career. Secondly, most of the main concepts of OOP—such as encapsulation, abstraction, and polymorphism—are applicable no matter what the language, even if the specifics of how they’re implemented change.
So, what you’ll learn today will certainly be transferable to other languages. With that in mind, let’s get started by first defining what an object is.
As the name implies, object-oriented programming revolves around the concept of object. So, nothing is more appropriate than to start our guide by defining this concept and giving examples.
In the context of object-oriented programming, an object is a cohesive unit of data and behavior associated with such data. An object represents a concept or entity in the given business domain the program resides on. The data it carries describe the characteristics or properties of said entity. The object’s behavior represents actions that can be operated on its data, and it’s expressed to external objects via the object’s methods.
How is programming with objects different than the alternatives? Pick procedural programming, for example. In that paradigm, there’s a stark divide between data and behavior. Generally speaking, in this paradigm, you have data structures that carry data around and don’t do anything and functions that operate on said data structures.
Let’s say you have a string and want to split it according to a delimiter—for instance, a comma. In a procedural programming context, you’d probably do something like this:
input = "123,asd,345" parts = split(input, ",")
The example above isn’t any particular programming language—it’s pseudo-code. As you can see, we called the split function and provided both the complete string and the delimiters as arguments.
In an OOP language, the string itself would be an object containing a split method. Here’s the same code, now in C#:
string input = "123,asd,345"; var parts = input.Split(",");
In many OOP languages, the class is a first-class concept. C# is one of those languages, so let’s examine now what a class is.
In simple terms, a class is a blueprint from which you create an object, a process we call instantiation. A class defines both the data and the behavior the objects created from it will have.
Let’s see a basic example of a C# class:
public class Person { private readonly string _name; private readonly int _age; public Person(string name, int age) { _name = name; _age = age; } public string Introduce() { return $"My name is {_name} and I'm {_age} years old."; } }
In the class above, we define both the data—the name and age fields—and the behavior—the Introduce method—that objects instantiated from the class will have.
And how do we create objects? By instantiating them, using the new keyword. Let’s see a few examples:
Person p = new Person("John Doe", 25); Person p2 = new Person("Mariah Silva", 32); Assert.AreEqual("My name is John Doe and I'm 25 years old.", p.Introduce()); Assert.AreEqual("My name is Mariah Silva and I'm 32 years old.", p2.Introduce());
In the class above, we made use of fields to store the person’s name and age. More specifically, we used private fields, which means other objects can’t access those values. What if we wanted to let other objects know those values? Would it be possible to define the fields as public?
Yes, and that would be terrible because it’d break encapsulation. You’ll read more about encapsulation in a minute, but for now, understand that an object’s internals is no one else’s business. If an object B knows about the internals of object A, then we say that B depends on A.
That means that when A changes, B probably has to change as well. In order to avoid that, we need to make use of encapsulation: the access to an object’s internal has to be protected in such a way that other objects aren’t affected in case a change in implementation occurs.
In C#, the mechanism we use for that is properties. Let’s see a new version of the previous class:
public class Person { private readonly string _name; private readonly int _age; public Person(string name, int age) { _name = name; _age = age; } public string Introduce() { return $"My name is {_name} and I'm {_age} years old."; } public string Name { get { return _name; } } public int Age { get { return _age; } } }
Now, via properties, we define getters that return the value of the private fields in a safe way. As you can see, we simply return the values as they are, so you might wonder why to bother.
Let’s imagine that some changes made it necessary to split the _name field into _firstName and _lastName. If our clients—that is, the objects that use our class—know about the fields, they would have to change. But if they only depend on the property, we can make this instead:
public class Person { private readonly string _firstName; private readonly string _lastName; private readonly int _age; public Person(string firstName, string lastName, int age) { _firstName = firstName; _lastName = lastName; _age = age; } public string Introduce() { return $"My name is {Name} and I'm {_age} years old."; } public string Name { get { return $"{_firstName} {_lastName}"; } } public int Age { get { return _age; } } }
The property now hides the fact that, internally, the name is split into two fields. (Of course, the constructor for the class now gets the two parameters, which means clients would still need to change, but you can get the idea.)
Methods are the functions that we define in classes and, generally speaking, operate on the data of the object. They have different visibility options: the main ones are private, internal, and public, but there are several more.
This provides essential features without describing any background details. Abstraction is important because it can hide unnecessary details from reference objects to names. It is also necessary for the construction of programs. Instead of showing how an object is represented or how it works, it focuses on what an object does. Therefore, data abstraction is often used for managing large and complex programs.
This binds the member function and data member into a single class. This also allows for abstraction. Within OOP, encapsulation can be achieved by creating classes. Those classes then display public methods and properties. The name encapsulation comes from the fact that this class encapsulates the set of methods, properties, and attributes of its functionalities to other classes.
This is the ability of an object to perform in a wide variety of ways. There are two types:
1. Dynamic polymorphism (runtime time). You can obtain this type through executing function overriding.
2. Static polymorphism (compile time). You can achieve static polymorphism through function overloading and operator overloading.
Within OOP, polymorphism can be achieved using many techniques including:
Through inheritance, a class can “inherit” the characteristics of another general class. To illustrate, dogs are believed to be the descendants of wolves. All dogs have four paws and can hunt their prey. This function can be coded into the Wolf class. All of its descendants can use it. Inheritance is also an is-kind-of relationship. For instance, a golden retriever is a kind of animal. Here’s an example:
class BaseClass { } class DerivedClass : BaseClass { }
We’ve covered several topics related to classes but not how to create one. Let’s rectify that now.
All you have to do to create a class is to add a class file to your project. The next step is to right-click on your project within the solution explorer and click Add, then choose New Item. You’ll see a new window. On the left side of the window, click Class in the Code template. Select a name for your class and click Add. It looks like this:
namespace OOPLearning { class Cat { } }
A method is an action an object can execute. In most instances, you can declare a method within a class definition. Yet, C# supports extension methods that let you add methods to an existing class outside the definition of a class.
Here’s an example of a simple C# program called “Hello World” from C# Corner that illustrates many of OOP fundamentals:
using System; namespace oops { //class definition public class SimpleHelloWorld { //Entry point of the program static void Main(string[] args) { //print Hello world" Console.WriteLine("Hello World!"); } } }
In order for a programming language to be object-oriented, it must have the ability to work with classes and objects. Moreover, it must use the fundamental object-oriented principles of abstraction, inheritance, polymorphism, and encapsulation.
For more information on OOP concepts in C# and how you can best utilize OOP, visit the following resources and tutorials:
In the short and medium terms, we expect C# and .NET to continue being highly influential, so it’s worth learning the fundamentals if you’re not already familiar. Check out some of our other posts for more basics and advanced concepts in C#, such as throwing C# exceptions, how to handle C# exceptions, catching them and finding application errors, and other tips.
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]