DEV Community

Cover image for C# Linting and Formatting Tools in 2021
Sam Magura
Sam Magura

Posted on • Edited on

C# Linting and Formatting Tools in 2021

tl;dr: Use SonarLint and optionally StyleCop.

The JavaScript ecosystem has amazing tools for formatting and statically analyzing your code: Prettier and ESLint. Both tools have nearly universal adoption and deliver significant value.

But what about linting and formatting C# code?

For formatting, there's Visual Studio's autoformatter (Edit > Advanced > Format Document), but it only formats a single file at a time and mainly fixes indentation and whitespace issues. It doesn't split up long lines, refuses to format certain language constructs, and is generally far less opinionated than Prettier.

For linting, the C# compiler already has a few lint-like warnings, such as reminding you when you've forgotten to await an async method. These are helpful but only scratch the surface when compared to ESLint.

Given the limitations of these built-in solutions, I set out to find something better.

Formatting: dotnet-format 🤔

dotnet-format is a formatting tool that's being included in the upcoming .NET 6 SDK. If you're not on .NET 6 yet, you can still easily install dotnet-format with dotnet tool install dotnet-format (pass the -g option for a global install). Johnny Reilly has a great post about setting up dotnet-format together with lint-staged.

I was really excited about dotnet-format when I heard about it, but sadly, it did not live up to the hype. I used it to reformat a medium C# 9 codebase and inspected the diff. And... all dotnet-format did was remove extraneous whitespace. dotnet-format did not

  • Break up long lines of code
  • Enforce consistent placement of curly braces
  • Add or remove blank lines
  • Reformat method signatures & calls with inconsistent argument formatting such as
  void AddDocument(string name, FileId fileId, long fileSize,
      DocType type,
      bool discoverable,
      CategoryId? categoryId
  );
Enter fullscreen mode Exit fullscreen mode

In dotnet-format's defense:

  • It was extremely easy to set up.
  • It can be completely automated — no manual intervention required.
  • My test codebase was already formatted pretty well.

Formatting: StyleCop 🙂

StyleCop.Analyzers is a open source suite of C# code analyzers that is installed via a NuGet package. When you first install StyleCop and rebuild your solution, you'll likely see 10,000+ warnings. Wow! But don't worry; StyleCop has automatic fixes for most of these, and many of warnings are about trivial things like sorting your using statements alphabetically.

Setup Instructions

Create a Directory.Build.props file next to your solution file and paste in the following XML.

<Project>
  <ItemGroup>
    <PackageReference 
      Include="StyleCop.Analyzers" 
      Version="1.2.0-beta.354"
      PrivateAssets="all" 
      Condition="$(MSBuildProjectExtension) == '.csproj'" 
    />
  </ItemGroup>
</Project>
Enter fullscreen mode Exit fullscreen mode

This adds the StyleCop.Analyzers NuGet package to every project in your solution. You should update the version number to whatever the latest is on NuGet.org. 1.2.0 introduces support for the latest C# features, so I recommend it even though it is still technically in beta.

Next, add an .editorconfig file, also in the same directory as your solution file. .editorconfig is Microsoft's recommended way of configuring analyzers — .ruleset files are now deprecated. You can use Visual Studio's .editorconfig GUI to tweak the Formatting and Code Style settings, but a text editor is much more convenient for configuring analyzers. Here's my .editorconfig file, which you're welcome to copy.

Finally, rebuild the solution. StyleCop's suggestions will appear in the Error List and you'll get green Intellisense squigglies.

Fixing StyleCop's Suggestions

StyleCop's default ruleset is extremely opinionated and I recommend disabling rules that don't bring value to your team. For example, the SA1200 rule requires that using statements be placed within the namespace declaration. While there's nothing wrong with this coding style, it's not widely used since Visual Studio puts using statements outside the namespace when you create a new C# file. Both conventions are equally valid, so I recommend disabling this rule. You can do this by adding a line to your .editorconfig:

# Using directive should appear within a namespace declaration
dotnet_diagnostic.SA1200.severity = None
Enter fullscreen mode Exit fullscreen mode

As you go through each StyleCop violation, you will encounter rules that are actually helpful. In every instance, I was able to automatically fix the violations across my entire solution via the quick fix menu.

My Review

StyleCop is a good tool and I recommend using it.

I didn't agree with many of StyleCop's rules (27 to be precise), but, it really cleaned up my codebase and the automatic code fixes are awesome. I am slightly worried that StyleCop will become a distraction when I get back to real development, but we'll see.

As the name suggests, StyleCop is primarily concerned with code style, not correctness. If we want to fix bugs in our code, we'll have to keep looking...

Linting: SonarLint 🤩

SonarLint is an IDE extension and NuGet package that analyzes your code for bugs, vulnerabilities, and code smells. The core product is open source, though the company seems to be pushing the IDE extension which integrates with their commercial products.

I tried out both the extension and the NuGet package and liked the NuGet package much better. Both options gave me Intellisense warnings, but only the NuGet package, SonarAnalyzer.CSharp, immediately showed me all the rule violations across my codebase. Moreover, a NuGet package is superior in a team setting since it will be downloaded automatically during build.

Setup Instructions

SonarAnalyzer.CSharp is a collection of Roslyn analyzers just like StyleCop, so the setup is very similar. I'll be showing configuration files that include both StyleCop and SonarLint, but you can totally use SonarLint on its own.

Edit the Default.Build.props file next to your solution to install SonarAnalyzer.CSharp into all projects:

<Project>
  <ItemGroup>
    <PackageReference 
      Include="StyleCop.Analyzers" 
      Version="1.2.0-beta.354"
      PrivateAssets="all" 
      Condition="$(MSBuildProjectExtension) == '.csproj'" 
    />
    <PackageReference
      Include="SonarAnalyzer.CSharp"
      Version="8.29.0.36737"
      PrivateAssets="all"
      Condition="$(MSBuildProjectExtension) == '.csproj'"
    />
  </ItemGroup>
</Project>
Enter fullscreen mode Exit fullscreen mode

Your SonarLint rule customizations will go in the same .editorconfig file we created earlier. Here's my completed .editorconfig. You can safely delete the StyleCop stuff if you're not using it.

Once that's done, rebuild the solution and you should see new warnings.

Fixing SonarLint's Suggestions

As you go through each warning, you'll notice that some have automatic fixes while others don't. If you encounter a rule you don't like, you can disable it using the same method I showed for StyleCop. I generally agreed with SonarLint's suggestions and only disabled 8 out of the 409 rules.

When you encounter a rule violation you don't know how to fix, consult SonarLint's extensive rule database.

My Review

The SonarLint NuGet package was everything I hoped for and I strongly recommend it.

By addressing each SonarLint warning, I was able to

  • Learn about a C# pitfall that can easily result in bugs
  • Identify several weak tests that made no assertions
  • Have TODOs in the code appear as warnings in the Error List
  • Fix a ton of inconsistently-named variables
  • Fix methods that weren't using one of their arguments
  • Add missing static modifiers to classes that only had static methods
  • And more!

Linting: ReSharper 🙁

ReSharper is a Visual Studio extension from JetBrains that adds advanced refactoring and static analysis. I used ReSharper for the first two years of my professional career, and found it to be a very helpful tool for learning C#'s advanced features. That said, I stopped using ReSharper due to its multiple downsides and I haven't looked back.

  • ReSharper starts at $129/year for individuals and $299/year per user for organizations.
  • Visual Studio + ReSharper is a slow and buggy mess.
  • Visual Studio's built-in refactorings have been steadily catching up to ReSharper's.
  • Once you're experienced with C#, ReSharper's warnings can be annoying rather than helpful.

JetBrains does have their own .NET IDE, Rider, which may be worth checking out.

Conclusion

I highly recommend SonarLint for identifying bugs and code smells.

I'll be using StyleCop to enforce code formatting best practices, though dotnet-format is also a viable option. It's a tradeoff: StyleCop is more powerful but requires babysitting. dotnet-format is easy to install and can be completely automated as part of a precommit hook, but it won't fix many common style issues.

Further Reading

Update 2021-10-05

  • Replace the now-deprecated .ruleset files with .editorconfig. Huge thanks to Honza Rameš for pointing this out!
  • Clarify the pricing of ReSharper. Thanks Valentine Palazkov.

Top comments (18)

Collapse
 
valentinepalazkov profile image
Valentine Palazkov • Edited

Thank you for the research - it's very useful.

It worth to mention that ReSharper costs $299 for organizations.

As for me, I have dotUltimate personal subscription that costs me $89/year and has much more than just ReSharper - it includes pretty much everything that C# developer would need.

But you are right that ReSharper is worthless for a project more than 300K lines of code because of performance issues.

I checked VS2022 + ReSharper - it's much better, I would say.

My guess that it's because VS2022 is a 64bit process.

But anyway, I switched to JetBrains Rider almost 4 years ago and never looked back.

Collapse
 
shadowcs profile image
Honza Rameš • Edited

Great article, thanks.

However I'd advice against using rulesets at this time and migrate to .editorconfigs which offer more options (but you'll loose the GUI for editing rulesets - which have its quirks for .NET Core projects anyway). See docs.microsoft.com/en-us/visualstu.... You can port between the two, see github.com/dotnet/roslyn/issues/41393.

Also there are two more analyzers worth mentioning: Roslynator and FxCop. FxCop is now part of the .NET SDK (since 5.0) and renamed to .NET analyzers. Several rules have been discontinued and a lot of rules (or all, depends on TargetFramework) are disabled by default. See docs.microsoft.com/en-us/dotnet/fu....

Collapse
 
srmagura profile image
Sam Magura

Thanks for bringing this up Honza. I did not know you could configure rules via .editorconfig. I'll try to get that working and then update the post accordingly.

Reference on how to use .editorconfig to configure rules: docs.microsoft.com/en-us/visualstu...

Collapse
 
binarypatrick profile image
BinaryPatrick

Love this post, thank you for all the research. Have you looked into any solutions adding this to a build pipeline or check in process? I'd really love to have an automated task in the PR to run this and commit styles changes to keep everything correct.

Collapse
 
srmagura profile image
Sam Magura

Thanks! The Johnny Reilly post I linked shows how to automate dotnet-format with a precommit hook. I don't think it will be possible to automate any of the other tools to the same extent.

Since SonarLint and StyleCop generate build warnings, you will see those warnings in CI. Here's a screenshot of a Sonar warning from my Azure Pipelines build:

Pipelines

Collapse
 
belav profile image
Bela VanderVoort • Edited

If you want an opinionated formatter for c# you should check out github.com/belav/csharpier, it is coming along nicely. The formatting is fairly stable at this point, I am mostly just dealing with edge cases.

Collapse
 
twofingerrightclick profile image
twofingerrightclick

This is definitely my favorite tool. Hands off. Makes for those who do a lot of Typescript.

Collapse
 
tomasforsman profile image
Tomas Forsman • Edited

Best post on the subjekt I've come across. Very well put together! It's highly appreciated.

Collapse
 
srmagura profile image
Sam Magura

Thank you!

Collapse
 
johnnyreilly profile image
John Reilly

Thanks for the link to my Prettier post Sam! I've since written a follow up more specifically on linting as I've been digging into the wonderful world of Roslyn Analyzers. blog.johnnyreilly.com/2022/04/06/e...

Collapse
 
ohritz profile image
Sohan Fernando

Just to add another tool to the mix.
jetbrains.com/help/resharper/ReSha...
is free.
We are just starting to try it out, to see if we can do some style cleanup in bulk on older codebases.

Collapse
 
aliki212 profile image
AlikiP

Great article!Thank you for the information and documentation!I am still learning so I'll stick with Resharper for now, good to know SonarLint for the future, cheers!

Collapse
 
ptkhuong96 profile image
PTKhuong96

Thanks. it's great

Collapse
 
alirezanet profile image
AliReZa Sabouri • Edited

Hi, I think you should combine these with husky.net, check it out
github.com/alirezanet/Husky.Net