Click here to Skip to main content
15,881,655 members
Articles / General Programming / Threads
Technical Blog

C# modifiers use and abuse – Part 2

Rate me:
Please Sign up or sign in to vote.
1.77/5 (4 votes)
14 Nov 2018CPOL8 min read 3.6K   1  
Hello and welcome to part two of the use and abuse of the C# modifier keywords, Last time we looked at the access modifiers that apply to types (class, struct, and enums) and members (fields, methods, nested classes, etc.

Hello and welcome to part two of the use and abuse of the C# modifier keywords,

Last time we looked at the access modifiers that apply to types (class, struct, and enums) and members (fields, methods, nested classes, etc.), this time we will have a look at some additional keyword modifiers that are used and abused. Let’s begin.☺

static

When this modifier is applied to classes it means that that class is never instantiated, instead, it is used as a container for fields and methods. The reason behind this is that static classes can only have static members and the members are associated with the type and not an instance.

Do note that a normal class can have both static and instance methods, but a static method cannot reference or use an instance member because there might not be an instance created onto which to use those methods.

Also, a static class cannot be part of a hierarchy chain, and the reason is, if you can’t instantiate it, it cannot be inherited from, nor can a static class inherit from another class, even if the other class is static as well.

All the restrictions specified above are also enforced by the compiler so it’s not a matter of preference. Though what is a matter of preference is its overuse.

It is my opinion that static classes should only be used for extension methods to make the code cleaner, or for a place to store constants across the application and even those should be used with a grain of salt. The issue with static are the following:

  • Static classes (especially those that hold data) are not garbage collected so they stay in memory for the duration of the whole application.
  • Static methods and classes cannot be extended, so when you need to make a change later down the line, any change will affect all occurrences where it is used.
  • It breaks the chain of dependencies, this means that when we have a class that invokes a static method or common resource, then we do not know if changing something will break our application, or even a chain of applications if it’s a common library.

Though all is not lost, there are two ways (and maybe many more depending on the scenario) we might mitigate this, the latter is the better of the two but that requires an additional system of which we will see shortly. The two methods are as follows:

  1. The Singleton Pattern. In this pattern we have a class that has a private constructor, this ensures that no one can control the instantiation of the class except itself, and a static field or property that give access to the previously mentioned instance.
    • The benefit of this approach, even if it’s a small one, is that an instance class used as such can be subclassed and abstracted giving more room to change and extend.
    • Also since we’re on the topic, please use properties for singletons, I have seen scenarios where a method was used to retrieve the instance, though not an inherently bad approach, during debugging you can inspect and even step over a property getter where as for a method you have to step into it and then back out if it’s used in a chain of calls.
  2. Using an Inversion of Control container (we will look into those in a future post), it is true that it will still be a singleton in the end but that gives you the option of changing the implementation any time you require it, even at runtime. It also encourages the use of constructor or method dependency injection instead of being called arbitrarily in the code.

readonly

I admit, if it weren’t for Reshaper I might not even use this as often as I should. When we apply this modifier to a field it means that that field can only be initialized in the constructor of the class.

The advantage of this is subtle but it’s there. By doing this, when you need to change the state of an internal variable outside the constructor you will have to figure out if that is actually the best course of action or if there is a better, cleaner way.

Some of you might have heard the increasing interests in functional programming, and the reason for that is simplicity, immutability, and multi-threading. When we have a class that changes state it’s harder to debug without knowing the steps that were taken before arriving at the state that cause the error, it is also easier to run the code on multiple threads due to the fact that each thread will work with the same resources and not modify them.

const

This one falls under the same category as the readonly keyword, but unlike reaonly, it means that a value is constant and it is initialized not at run time, but during compile time.

This is why any magic string like a popup message or any magic number should ideally be defined as a constant, to ensure that the message or number doesn’t change during the course of the execution creating an issue later down the road.

This also brings about a small performance boost since all constants are processed when the code is being compiled and are stored in a separate part of memory than when the application is running.

sealed

This modifier can be used on classes and members and means that the inheritance chain cannot continue after that point. This is a good approach for framework developers since it lets you control when a class can be subclassed or when an overridden method should not be implemented lower down the inheritance chain.

If you’re one of the developers which turn warnings as errors on or just doesn’t ignore the warnings the compiler offers you, you will notice that when you try to use an overridable method inside a constructor it tells you that it is not safe to do that because due to the polymorphic nature of objects, you don’t know if that class has been implemented or not, that also includes in sub-classes over which you may not have control over if you wrote it in a framework and a developer uses it badly like throwing a NotImplementedException. The solution, in this case, is either making the class itself sealed or sealing the implementation of that overridable method.

As a good practice, try putting everything sealed by default, so that when you do need to sub-class you will have to change it and as such maybe ask yourself “why do you need that?”, don’t discredit why something needs to be done, but try to understand the reason behind it and then if needed discredit the reason or not based on necessity.

abstract

The abstract key word is used to declare that a class cannot be instantiated and can only serve as a base class, so where and how do we use it and why isn’t it enforced more often? Well, let’s have a deeper look at this modifier.

Any function (that includes methods, indexers, properties, and events) declared as abstract must be implemented in the non-abstract classes (you can skip implementing the members if the inheriting class is abstract itself) that inherit from an abstract class because when one a method or function is declared as abstract then you cannot implement it as well (by implement I mean it cannot have logic or a method body associated with it when it’s declared).

That means that any member that is defined as abstract must be declared inside an abstract class, the reason behind this, if we think about it, makes a lot of sense. Consider you have a class that can be instantiated but has an abstract method, since we don’t have an implementation for that method then that class might be broken and cannot be used properly, as such the compiler enforces the fact that only abstract classes can have abstract members.

An abstract class can contain public, protected, protected internal or internal members that are not abstract since these are inherited implicitly (can have private members as well but those only serve as local helpers). With this said I will show you a nice trick that combines both abstract and sealed through an example and analyze it.

A class cannot be both abstract and sealed because that would defeat the purpose of the class since it cannot be inherited from but neither can it be instantiated, kinda like having a private class at the namespace level.

Though what we can have is a base abstract class that defines a set of abstract methods and a public method for the workflow it needs to do.

Here we have the AccountBase class that has a public method which defines the workflow for a class and two abstract methods which just plug in at certain key moments to update or modify the workflow of the CalculateTax method. Then we have two subclasses that are declared as sealed which implement the two abstract methods both with a sealed keyword.

Take note that by using this design, we have implemented the Template pattern, and as such, no matter on which subclass the public method is called on we will be sure that the workflow is respected and all the mandatory abstract elements are implemented.

That concluded this two part series of about modifiers use and misuse in the field, there are more modifiers present in the language though these are the most used, like in the case of static, or misused as in the case of the internal, const and readonly modifiers.

Thank you and I will see you next time,

Vlad V.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Romania Romania
When asked, I always see myself as a .Net Developer because of my affinity for the Microsoft platform, though I do pride myself by constantly learning new languages, paradigms, methodologies, and topics. I try to learn as much as I can from a wide breadth of topics from automation to mobile platforms, from gaming technologies to application security.

If there is one thing I wish to impart, that that is this "Always respect your craft, your tests and your QA"

Comments and Discussions

 
-- There are no messages in this forum --