New day, new style

Spread the love

I have used StyleCop for years. The project started back in the mid-2000s as an internal Microsoft tool, was released to the world in 2008, and became open source on CodePlex in 2010.

As you can imagine, coding style can be a very personal and contentious issue. There are proponents and detractors alike, each explaining why StyleCop is either essential or detrimental.

I will go on record as saying I have gotten a lot of value out of StyleCop. It trained me to write code in a very particular but consistent way, so that I don’t have to spend cycles thinking about where class members should go, how to format braces, and so on. Since the advent of StyleCop, however, a lot has changed in the state of the art of code styling in .NET. For example, we now have EditorConfig with full integration in Visual Studio as well as dotnet build / MSBuild.

Does that mean StyleCop is obsolete? Well, not exactly. The existence and continued development of StyleCopAnalyzers shows that it is still an active, viable technology. I have used this incarnation of StyleCop and would recommend it to anyone who still prefers “the StyleCop way” on modern projects.

That being said, the .NET team — i.e., those who actually build and maintain .NET as a platform — has never really embraced StyleCop. The C# coding style adopted within the .NET Runtime itself has many differences from the StyleCop mandates. Let’s go through a few of these differences along with plausible explanations for why these style choices are defensible.

Usage of this

StyleCop says always use this when accessing instance members. The .NET Runtime says never, unless absolutely necessary. Who is right? This rule is surprisingly controversial, and my anecdotal evidence suggests that most people don’t like overuse of this. The best argument in favor of this is consistency and clarity. But we can probably get by without it if we also adopt the .NET Runtime rule of prefixing instance fields with underscore, e.g. _myVal = myVal. Now we have a clear delineation between instance data and locals or parameters and _ serves as a shorter substitute for this. We do however lose the ability to tell the difference between static and instance properties and methods, absent other context. But on the plus side, if we change whole code paths from instance to static members, the refactoring pain is minimal (since we don’t have to delete a bunch of this prefixes).

Placement of using

StyleCop says to put using directives inside the namespace, whereas .NET says to do so outside. The StyleCop justification is documented in the corresponding rule SA1200 — basically that outer namespace directives can lead to ambiguity when there are type name clashes. But wait, the C# Coding Conventions seem to argue the opposite, that a “using directive placed inside a namespace is context-sensitive and complicates name resolution.” Despite having written thousands of usings in the StyleCop way, I actually side with the .NET Runtime here. Unless we’re willing to put global:: in front of every namespace, it does seem clearer to just do it outside.

Order of members

StyleCop is rigid here — public, protected, private; fields before constructors before properties, etc. The .NET Runtime is mostly silent here, other than to recommend that fields “should be specified at the top.” I did like the StyleCop way because it was absolutely consistent and left little room for debate or choice. However, you (and Emerson) could say this is “a foolish consistency.” If we value cohesion then maybe we would want things that are related to be close together, even if the related items are of different visibilities (say, a large public method whose parts have been extracted into nearby private methods).

Alas, EditorConfig doesn’t even support element ordering. So this style rule may be moot in the modern world.

One type per file

This may be the most controversial style rule. Java was one of the first to mandate one (public) type per file. On the surface, the reasoning would seem to be one of tidiness, that it is simply easier to tell where things are in a large project if the file MyClass.java has one class called MyClass. But the actual history seems to be more practical:

“[I]t’s necessary for efficient package importation.” […] the compiler would have to make an additional pass through all the compilation units (.java files) to figure out what classes were where, and that would make the compilation even slower.

Given that we have better compilers now, what is the use of the rule? The consensus, if there is any, is that you should not have “too many” types in the same file. This is as true for C/C++ as it is for Rust.

So, then, how many types per file? StyleCop would say “exactly one” is the right answer here. The .NET folks do not specify a hard and fast rule. Most of the time you will see one type per file, but there are notable exceptions especially around extension methods where partial classes are favored (see: LINQ) and with delegate types (see: Action). But as with element ordering, this rule anyway cannot be enforced with EditorConfig alone.

Conclusion

We’re on .NET 7 now, and heading toward .NET 8. The world is different from the .NET Framework 2.X world in which StyleCop was born. I will personally start phasing out my use of StyleCop in favor of the more open and cross-language applicability of EditorConfig. My new code samples repo writeasync2 will reflect this choice going forward.

Leave a Reply

Your email address will not be published. Required fields are marked *