Polymorphism in C#

Problem
You want to know about polymorphism in C# along with its two types.

Solution
In general, polymorphism means possessing many forms. In C#, it means one interface, many functions. There are two types of polymorphism in C#. They are compile-time polymorphism and run-time polymorphism. Compile-time polymorphism is also referred to as static or early binding. Run-time polymorphism is also referred to as dynamic or late binding.

Discussion
A practical example of compile-time or static polymorphism is the addition operator. When using the addition operator with numeric data, a mathematical operation occurs. When using the addition operator with string data, concatenation occurs. This example illustrates operator overloading. Method and operator overloading comprise compile-time polymorphism in C#. Abstract classes and virtual methods provide a means for run-time polymorphism. These topics are out of scope for this blog entry. But, the gist is that calls to methods with many implementations get determined at run-time. Let’s say you generate a random number in your code. You have two methods. One gets called when the number is even, and the other gets called when it’s odd. Determining which method gets called is an example of run-time polymorphism.

Inheritance in C#

Problem
You want to know about object inheritance in C# and its key advantages.

Solution
Inheritance is a process where one type acquires the members of another type. The class that’s inherited from is the base or parent class in C#. The inheriting class is the child class. You can add more members to the child class and override their existing members. One advantage of inheritance is code reusability. Another is the removal of duplicate code.

Discussion
Consider the automobile and truck. A truck is an automobile, but it’s a sub-type of automobile. Not all automobiles have beds for hauling like trucks do. You could model this relationship in OOP and show inheritance. For example, the parent class Automobile would contain methods such as Drive() and Stop() as well as properties such as Color and Make. The child class Truck would inherit the methods and properties of the parent class Automobile but would contain more members such as BedLength.

Abstraction in C#

Problem
You want to know the purpose of abstraction and how it differs from encapsulation.

Solution
Abstraction allows developers to model their problems at higher levels. This way, they don’t have to worry about the implementation details of a type. Encapsulation is used to hide implementation details of a type from clients. Clients can be code or people that use the encapsulated code. Abstraction is related to encapsulation, but they are not the same. Abstraction is the process of generalization.

Comments
Remote controls are real-world examples of abstraction and encapsulation. The operations a remote can perform are abstractions, while the device itself provides encapsulation.

Encapsulation in C#

Problem
You want to know the purpose of encapsulation in OOP and how it works in C#.

Solution
There are two reasons for encapsulation. The main one is information hiding. Information hiding is enforced by restricting direct access to an object’s members. The second reason for encapsulation is grouping related data and methods together. A “capsule” is created when data and methods (the operations performed on the data) are coupled.

Comments
In C#, encapsulation is implemented through the use of access modifiers and properties.

Classes and Objects

Problem
You want to know what classes and objects are and the differences between them.

Solution
Classes and objects are at the core of Object-Oriented programming (OOP). A class is a blueprint or template for creating objects, and objects are instances of a class.

Comments
Structured programming aims to solve problems with functions. OOP solves them with objects. Think of objects as custom data types. Classes give programmers a means to define their own data types.

Examples
Your pet dachshund is an object of the Dog class.

Bulky Code

Problem
What should you do when the need to check many conditions results in deeply nested code?

Solution
Avoid nesting more than three levels when using conditional and looping constructs. A great resource on refactoring techniques for minimizing nesting is here.

Comments
Avoid deeply nested code because it’s difficult to understand and debug. Use static analysis tools to determine the cyclomatic complexity of your code.

Using the Obsolete Attribute

Problem
You have deprecated types or members, and you don’t want clients using them anymore. What should you do?

Solution
First, start by marking deprecated types and members with the Obsolete attribute. Do this in the first version released with the deprecated types. In subsequent versions, use the true argument of the Obsolete attribute to cause compile-time errors. This should force users to remove any reference to the member.

Comments
Use the Obsolete attribute’s Message property to specify what to use in place of the deprecated code. Also, don’t remove a type or member without marking it obsolete in at least one version of your library.

Example

// This usage causes a compilation warning.
[Obsolete("Use WaitForElement instead")]
void WaitForItem() {
    ...
}

// This usage causes a compilation error.
[Obsolete("Use WaitForElement instead", true)]
void WaitForItem() {
    ...
}