Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
The C# Workshop

You're reading from   The C# Workshop Kickstart your career as a software developer with C#

Arrow left icon
Product type Paperback
Published in Sep 2022
Publisher Packt
ISBN-13 9781800566491
Length 780 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (3):
Arrow left icon
Almantas Karpavicius Almantas Karpavicius
Author Profile Icon Almantas Karpavicius
Almantas Karpavicius
Jason Hales Jason Hales
Author Profile Icon Jason Hales
Jason Hales
Mateus Viegas Mateus Viegas
Author Profile Icon Mateus Viegas
Mateus Viegas
Arrow right icon
View More author details
Toc

How C# Helps with Object-Oriented Design

So far, the principles you have learned are not language-specific. It is time to learn how to use C# for OOP. C# is a great language because it is full of some very useful features. It is not only one of the most productive languages to work with, but it also allows you to write beautiful, hard-to-break code. With a rich selection of keywords and languages features, you can model your classes completely the way you want, making the intentions crystal clear. This section will delve deep into C# features that help with object-oriented design.

Static

Up till now in this book, you have interacted mostly with static code. This refers to code that does not need new classes and objects, and that can be called right away. In C#, the static modifier can be applied in five different scenarios—methods, fields, classes, constructors, and the using statement.

Static methods and fields are the simplest application of the static keyword:

public class DogsGenerator
{
    public static int Counter { get; private set; }
    static DogsGenerator()
    {
        // Counter will be 0 anyways if not explicitly provided,
        // this just illustrates the use of a static constructor.
        Counter = 0;
    }
    public static Dog GenerateDog()
    {
        Counter++;
        return new Dog("Dog" + Counter);
    }
}

Note

You can find the code used for this example at https://packt.link/748m3.

Here, you created a class called DogsGenerator. A static class cannot be initialized manually (using the new keyword). Internally, it is initialized, but only once. Calling the GenerateDog method returns a new Dog object with a counter next to its name, such as Dog1, Dog2, and Dog3. Writing a counter like this allows you to increment it from everywhere as it is public static and has a setter. This can be done by directly accessing the member from a class: DogsGenerator.Counter++ will increment the counter by 1.

Once again, note that this does not require a call through an object because a static class instance is the same for the entire application. However, DogsGenerator is not the best example of a static class. That's because you have just created a global state. Many people would say that static is inefficient and should be avoided because it might create unpredictable results due to being modified and accessed uncontrollably.

A public mutable state means that changes can happen from anywhere in the application. Other than being hard to grasp, such code is also prone to breaking in the context of applications with multiple threads (that is, it is not thread-safe).

Note

You will learn about threading in detail in Chapter 5, Concurrency: Multithreading Parallel and Async Code.

You can reduce the impact of a global state by making it publicly immutable. The benefit of doing so is that now you are in control. Instead of allowing a counter increment to happen from any place inside a program, you will change it within DogsGenerator only. For the counter property, achieving it is as simple as making the setter property private.

There is one valuable use case for the static keyword though, which is with helper functions. Such functions take an input and return the output without modifying any state internally. Moreover, a class that contains such functions is static and has no state. Another good application of the static keyword is creating immutable constants. They are defined with a different keyword (const). The Math library is probably the best example of helper functions and constants. It has constants such as PI and E, static helper methods such as Sqrt and Abs, and so on.

The DogsGenerator class has no members that would be applicable to an object. If all class members are static, then the class should be static as well. Therefore, you should change the class to public static class DateGenerator. Be aware, however, that depending on static is the same as depending on a concrete implementation. Although they are easy to use and straightforward, static dependencies are hard to escape and should only be used for simple code, or code that you are sure will not change and is critical in its implementation details. For that reason, the Math class is a static class as well; it has all the foundations for arithmetic calculations.

The last application of static is using static. Applying the static keyword before a using statement causes all methods and fields to be directly accessible without the need to call a class. For example, consider the following code:

using static Math;
public static class Demo
{
    public static void Run()
    {
   //No need Math.PI
        Console.WriteLine(PI);
    } 
}

This is a static import feature in C#. By using static Math, all static members can be accessed directly.

Sealed

Previously, you mentioned that inheritance should be handled with great care because the complexity can quickly grow out of hand. You can carefully consider complexity when you read and write code, but can you prevent complexity by design? C# has a keyword for stopping inheritance called sealed. If it logically makes no sense to inherit a class, then you should mark it with the sealed keyword. Security-related classes should also be sealed because it is critical to keep them simple and non-overridable. Also, if performance is critical, then methods in inherited classes are slower, compared to being directly in a sealed class. This is due to how method lookup works.

Partial

In .NET, it is quite popular to make desktop applications using WinForms. The way WinForms works is that you can design how your application looks, with the help of a designer. Internally, it generates UI code and all you have to do is double-click a component, which will generate event handler code. That is where the partial class comes in. All the boring, autogenerated code will be in one class and the code that you write will be in another. The key point to note is that both classes will have the same name but be in different files.

You can have as many partial classes as you want. However, the recommended number of partial classes is no more than two. The compiler will treat them as one big class, but to the user, they will seem like two separate ones. Generating code generates new class files, which will overwrite the code you write. Use partial when you are dealing with autogenerated code. The biggest mistake that beginners make is using partial to manage big complex classes. If your class is complex, it's best to split it into smaller classes, not just different files.

There is one more use case for partial. Imagine you have a part of code in a class that is only needed in another assembly but is unnecessary in the assembly it is originally defined in. You can have the same class in different assemblies and mark it as partial. That way, a part of a class that is not needed will only be used where it is needed and be hidden where it should not be seen.

Virtual

Abstract methods can be overridden; however, they cannot be implemented. What if you wanted to have a method with a default behavior that could be overridden in the future? You can do this using the virtual keyword, as shown in the following example:

public class Human
{
    public virtual void SayHi()
    {
        Console.WriteLine("Hello!");
    }
}

Here, the Human class has the SayHi method. This method is prefixed with the virtual keyword, which means that it can change behavior in a child class, for example:

public class Frenchman : Human
{
    public override void SayHi()
    {
        Console.WriteLine("Bonjour!");
    }
}

Note

You can find the code used for this example at https://packt.link/ZpHhI.

The Frenchman class inherits the Human class and overrides the SayHi method. Calling SayHi from a Frenchman object will print Bonjour.

One of the things about C# is that its behavior is hard to override. Upon declaring a method, you need to be explicit by telling the compiler that the method can be overridden. Only virtual methods can be overridden. Interface methods are virtual (because they get behavior later), however, you cannot override interface methods from child classes. You can only implement an interface in a parent class.

An abstract method is the last type of virtual method and is the most similar to virtual in that it can be overridden as many times as you need (in child and grandchild classes).

To avoid having fragile, changing, overridable behavior, the best kind of virtual methods are the ones that come from an interface. The abstract and virtual keywords enable changing class behavior in child classes and overriding it, which can become a big issue if uncontrolled. Overriding behavior often causes both inconsistent and unexpected results, so you should be careful before using the virtual keyword.

Internal

public, private, and protected are the three access modifiers that have been mentioned. Many beginners think that the default class modifier is private. However, private means that it cannot be called from outside a class, and in the context of a namespace, this does not make much sense. The default access modifier for a class is internal. This means that the class will only be visible inside the namespace it is defined in. The internal modifier is great for reusing classes across the same assembly, while at the same time hiding them from the outside.

You have been reading a chapter from
The C# Workshop
Published in: Sep 2022
Publisher: Packt
ISBN-13: 9781800566491
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime