C#'s Worst Naming Convention
It's not Hungarian Notation, but it's alarmingly similar!
Background
I’ve worked in many software houses and seem many “coding standards”, however one of them, which actually constitutes a naming violation seems to have become a trend in which, member variables are prefixed with a symbol, usually an underscore or some other moniker to denote that the variable is a private member of a class.
This trend seems to be somewhat localized to the C# developer community. I’m not entirely sure whether this trend applies to other programming languages, though having worked professionally with JavaScript, TypeScript, Java, Kotlin and C#, I can confirm that I have never seen this practice applied so religiously anywhere other than in C#.
This trend was recently (at the time of writing) identified in a Twitter Poll, and for the sake of completeness, here are the results of that poll:
Option | Result |
camelCase with _ prefix | 57% |
camelCase with no prefix | 38% |
camelCase with m_ prefix | 3% |
Something else | 2% |
According to that poll, 57% of the demographic would deem this code to be perfectly acceptable:
class Program
{
private readonly object _member;
public Program(object member)
{
_member = member;
}
}
For the record, I’m in agreement with 38% of the demographic (camelCase with no prefix). Opinions are obviously highly divided on this topic, however it’s one area of code quality that I am particularly passionate (or pernickety if you wish) about, and my aim here is to identify through fact finding, logical analysis and due diligence, why 62% of the demographic get this so horribly wrong.
History
Prefixing member variables with underscores is an archaic practice borrowed from C and C++ in the early days of C#. Bearing in mind that C was first released in 1972 and C++ in 1985, it was considered an acceptable practice in the days where IDEs and code analysis tools were less advanced, and it was not uncommon for source files to exceed 5,000 SLoC (Source Lines of Code).
In order to circumvent productivity problems, some common conventions evolved which allowed developers to identify parts of their code easily:
- Prefixing the variable’s name with its type (Hungarian Notation).
- Prefixing the variable’s name with a moniker to denote member scope.
IDEs and code analysis tools have come a long way since then, and with them the power to be more expressive and productive without needing to sacrifice readability or maintainability by using practices that should have become redundant by now.
You’ll hopefully have noticed that I mentioned Hungarian Notation, and with good reason; this is one such practice that has been actively discouraged in the .NET community for a long time, however sadly, many developers still insist on applying scope details to member names!
Justifications & Rebuttals
It reduces
this.
noise throughout the code base.
You don’t have to use this.
everywhere where you're referring to a member variable. You are only required to do so when you have a local variable in scope with the same name. In contrast, if you prefix your member variable with an underscore, you're forced to use that everywhere because it's tacked onto the name, rather than leaning on the language - by definition adding unnecessary noise. Why would Microsoft bother adding a keyword to the language, if not to be used appropriately?
It prevents collisions between member variables and local variables.
It doesn’t prevent collisions at all! There is nothing to stop you, me, or any other developer from introducing a local variable that is prefixed with an underscore. In contrast, this.
will never refer to a local variable. Additionally, the number of times that a local variable will have the same name as a member variable will be tiny.
Microsoft prefix their member variables with underscores.
The convention spilled over from C and C++. Many of the Microsoft developers working in the .NET space came from C and C++ and brought their conventions with them — that doesn’t make them right!
Some Microsoft employees have been outspoken about this very matter and have explained that they do not use, or are no longer using underscore prefixes, but some of the older developers who have been around for a long time still do as a matter of habit.
If you look at the .NET Framework source code, you’ll notice that there are far fewer underscore prefixes in newer versions of the framework. This suggests that Microsoft are slowly learning from historical mistakes.
Microsoft doesn’t have any particular preference, convention or stance.
Microsoft haven’t been explicit about their stance on member variable naming, however if you read the documentation on General Naming Conventions you’ll see that it specifically states “DO NOT use underscores, hyphens, or any other non-alphanumeric characters”.
It’s easier to identify a variable’s scope if it’s prefixed with an underscore.
It’s not the responsibility of a variable’s name to tell you about its scope. It’s the same rationale we used to discourage Hungarian Notation, because it’s not the responsibility of a variable’s name to tell you about its type.
It’s a Resharper default.
Well then, Jetbrains didn’t read the documentation either! If you’re following the herd with a delusional belief that Resharper is somehow making you write better code, then unfortunately you’ve lost the ability to reason about your own code, let alone anyone else’s. Stop letting opinionated, static analysis tools rule your professional life.
Readability & Semantics
Prefixing member variables with underscores does not improve readability or semantics — if anything it does the opposite. In the majority of cases the prefix denotes a private member variable, but they don’t have to. The meaning of the prefix could actually differ from one software house to another, so essentially the practice becomes a house style, even if most software houses agree on the style.
In contrast, most object-oriented, C-syntax languages provide you with the this
keyword to help you infer that something is a member, therefore offering greater semantic value over prefixes because you can tell exactly and immediately what it's referring to, and will always refer to a member because you can’t change the semantics of this
.
Compile-Time Checking
Prefixing member variables with underscores actually hinders compile-time checking whereas using this
doesn't. There is nothing to stop me from introducing a local variable that starts with an underscore. Everything will still compile, but the program will unlikely behave as expected. If however I introduce this
I'm free to introduce a local variable with the same name, without causing conflicts.
Other Languages
I’ve seen underscore prefixes used in JavaScript to denote that a member is intended for private or internal use. S̶i̶n̶c̶e̶ ̶J̶a̶v̶a̶S̶c̶r̶i̶p̶t̶ ̶d̶o̶e̶s̶n̶’̶t̶ ̶h̶a̶v̶e̶ ̶t̶h̶e̶ ̶n̶o̶t̶i̶o̶n̶ ̶o̶f̶ ̶a̶c̶c̶e̶s̶s̶ ̶m̶o̶d̶i̶f̶i̶e̶r̶s̶,̶ ̶e̶v̶e̶r̶y̶t̶h̶i̶n̶g̶ ̶i̶s̶ ̶p̶u̶b̶l̶i̶c̶ ̶b̶y̶ ̶d̶e̶f̶a̶u̶l̶t̶.̶ ̶I̶ ̶t̶h̶i̶n̶k̶ ̶t̶h̶e̶ ̶p̶r̶a̶c̶t̶i̶c̶e̶ ̶o̶f̶ ̶a̶p̶p̶l̶y̶i̶n̶g̶ ̶p̶r̶e̶f̶i̶x̶e̶s̶ ̶i̶s̶ ̶a̶c̶c̶e̶p̶t̶a̶b̶l̶e̶ ̶h̶e̶r̶e̶,̶ ̶s̶i̶n̶c̶e̶ ̶i̶t̶ ̶i̶l̶l̶u̶s̶t̶r̶a̶t̶e̶s̶ ̶i̶n̶t̶e̶n̶t̶ ̶i̶n̶ ̶a̶ ̶l̶a̶n̶g̶u̶a̶g̶e̶ ̶t̶h̶a̶t̶ ̶i̶s̶ ̶l̶a̶c̶k̶i̶n̶g̶ ̶a̶c̶c̶e̶s̶s̶ ̶m̶o̶d̶i̶f̶i̶e̶r̶s̶ ̶a̶s̶ ̶a̶ ̶f̶e̶a̶t̶u̶r̶e̶.̶
At the (original) time of writing, the struck-out text (above) was true. JavaScript now has a mechanism to denote private members; for example:
class Program
{
#member;
}
With the recent success and growth of TypeScript, C# developers are applying their object-oriented knowledge to front-end or NodeJS based projects. Unlike C#, TypeScript is strict about member access in that all references to members must be qualified with this.
or the type's name if they're static.
This makes underscore prefixes even more redundant because developers are forced to qualify members correctly. Unfortunately I’ve already seen examples such as this._member
on several occasions. This is really appalling code quality!
Summary
Remember that this is only my opinion...
Member variable prefixes are just horrible! The simple fact is that prefixing member variables with an underscore adds literally nothing to your code. You can wave goodbye to readability, maintainability, semantics and compile time checking, all because you probably didn’t read the documented convention, and instead, probably chose to use a tool or followed a herd that suggested that underscore prefixes are a good idea. this
exists for a reason, and it's a beautiful thing when used appropriately.