Formatting conventions for primary constructors
A discussion on how the C# community should format code that uses primary constructors

C# 12 is introducing a concept called primary constructors. This will allow developers to define constructor parameters alongside the class definition, and have them in scope for the entire class.

I'm not sure how I feel about this feature, but it exists so we may as well get used to it. There's one issue with the concept that concerns me, though: How to format class definitions that use primary constructors. Nick Chapsas touches on the issue in his video here. I'd recommend watching the video - Nick introduces an example CachedWeatherHandler class using primary constructors, and asks for community feedback on five different ways the code could be formatted. I've reproduced his five options below:

Option A

public class CachedWeatherHandler(IMemoryCache cache, ILogger<CachedWeatherHandler> logger) : DelegatingHandler
{
    // ...
}

Option B

public class CachedWeatherHandler(
    IMemoryCache cache,
    ILogger<CachedWeatherHandler> logger) : DelegatingHandler
{
    // ...
}

Option C

public class CachedWeatherHandler(
    IMemoryCache cache,
    ILogger<CachedWeatherHandler> logger
    ) : DelegatingHandler
{
    // ...
}

Option D

public class CachedWeatherHandler(
    IMemoryCache cache,
    ILogger<CachedWeatherHandler> logger)
    : DelegatingHandler
{
    // ...
}

Option E

public class CachedWeatherHandler(
    IMemoryCache cache,
    ILogger<CachedWeatherHandler> logger) :
    DelegatingHandler
{
    // ...
}

Community feedback on Nick's video seems to be leaning towards Option C or Option D.

However, I would suggest that the community embrace a slightly different option. Building on Option C, but with slightly different placement of the parenthesis.

The best option (according to Matt)

public class CachedWeatherHandler(
    IMemoryCache cache,
    ILogger<CachedWeatherHandler> logger
) : DelegatingHandler
{
    // ...
}

public class CachedWeatherHandlerWithMultipleInterfaces(
    IMemoryCache cache,
    ILogger<CachedWeatherHandler> logger
) : DelegatingHandler,
    InterfaceOne,
    InterfaceTwo
{
    // ...
}

The advantage of this option is that it is a bit more clear on the delineation between the constructor parameters and the list of implemented classes and interfaces. A few commentors on Nick's YouTube video made (or endorsed) similar suggestions. Shoutout to @vorontsovru270895, @marikselazemaj3428, and @MarcSelman - great minds think alike.


Posted by Matthew King on 1 October 2023
Permission is granted to use all code snippets under CC BY-SA 3.0 (just like StackOverflow), or the MIT license - your choice!
If you enjoyed this post, and you want to show your appreciation, you can buy me a beverage on Ko-fi or Stripe