Home > dotnet > Towards a Beginner-Friendly DotNet

Towards a Beginner-Friendly DotNet

Adopting beginner-friendly Configuration Settings for new C# Developers

by
Published: Last Updated on

I love dotnet. I've been happily using it since the betas and even get to teach it professionally as my day job. However, dotnet has 20+ years of baggage for new learners and I'd like to try improving that experience for those coming into the field using Visual Studio features that are already present.

In this piece, I'll introduce a set of setting choices that takes aim at some default values, hides entire language features, and probably makes me a few enemies. I'll also provide an .editorconfig file to make it easier to include these settings in your projects in the future.

This content is also available on YouTube.

The DotNet Learning Curve

I love dotnet, but every class I teach is a firm reminder that dotnet and Visual Studio are not as beginner-friendly as I wish it was.

Todays learners are learning the same syntax, concepts, and tooling that I learned two decades ago. However, these students also have 20+ years of new language features to learn. When learners search they get code snippets that contain a wide degree language features such as null-coalescing, nullable values, lambda expressions, pattern matching, records, and more.

While it's good for new learners to discover these language features and grow into these concepts, it is not good for them to be drowning in new syntax while still trying to internalize how to write a for loop.

This is something that I believe a set of consistent language rules for new learners could help focus the efforts of new learners onto the core of the language.

Beginner-Friendly Visual Studio Settings

Let's go over the individual choices I recommend for a more beginner-friendly C# learning experience.

To get to these choices, you'll need to open up Visual Studio and then select the Tools menu. From there, click Options... at the bottom.

Once the options dialog opens, expand Text Editor on the left sidebar, then find C# and expand it. Finally, select Code Style or the General tab inside of Code Style. This will give you a long list of rules.

We'll go over each group of rules and my recommendations for them in the following sections.

"This" Preferences

The four this settings govern the use of the this keyword in C#.

As a new dotnet developer I benefitted significantly from using the this keyword to give me good IntelliSense suggestions on methods and properties I was inheriting.

I now teach dotnet professionally and have seen many students be more confused by the unnecessary presence of the this keyword than helped by its functionality.

As a result, my policy is to recommend students use this only in constructors to protect them against mistakes like the following code:

public class Car
{
   private string color;


   public Car(string color)
   {
      color = color;
   }
}

The code above is incorrect because color = color refers to the same parameter on the left and the right. In this case this.color = color; is the correct form of the code.

Visual Studio doesn't give us an option to require this in constructors only, so my recommendations here are simply to leave all four this preference settings as "Do not prefer 'this.'" and leave them as refactoring suggestions.

However, I could certainly entertain arguments that "Qualified field access" could be set to "Prefer 'this.'".

Predefined Type Preferences

The predefined type preferences are a minor detail focusing on whether you should use common types such as int or their more formal Int32 type names.

Both ways the same thing and work, but it is far more common to see the shorthand types in dotnet code.

Because of this, I recommend preferring predefined types and treating deviations from this as warnings since they will actively distract new devs.

Var Preferences

The var keyword in C# is often divisive for teams. My old stance on var was to tolerate it only for complex type initialization such as the following code:

var playerGoals = new Dictionary<string, int>();

However, with C#'s target-typed new keyword this use case is no longer my recommendation and I instead recommend the following code:

Dictionary<string, int> playerGoals = new();

You can read my article on target-typed new for more details as to my reasoning, but it boils down to this: var is distracting and takes a moment to mentally determine the type. Let's be nice to newer developers and not hide types behind var keywords if we can help it.

There's an argument to be made for typing less when creating code, but the target-typed new keyword satisfies this need nowadays.

As a result, I advocate for preferring against var and warning when it is present.

Code Block Preferences

Code block preferences focus on a few different aspects of C# code blocks.

To simplify the discussion for those skimming this, here's my table of recommendations:

Let's discuss each one of these a bit more.

Prefer Braces

Prefer braces governs whether you should include { } scopes around single line statements.

For example, the following C# code is legal:

if (amount < 0)
   Console.WriteLine("You put in a negative number");

I have seen many students discover that the { } scopes are optional around blocks of code and try to omit them (less code must be better, right?) only to be confused by their own program later on.

Whitespace and indentation are not syntactically meaningful in C#, and this is one of those early areas that new learners frequently get lost at sea.

By making Prefer braces set to Yes in all cases and warning when it is not, we help flag this behavior earlier on to help learners avoid troubled waters.

Namespace Definitions

Dotnet now gives us the ability to use a scope-free namespace declarations that are scoped to files. It's a newer feature and I rather like it. However, historical code and examples more commonly use the { } scoped notation for namespaces.

My suspicion is that developers will ultimately have a better experience with file-scoped namespace definitions due to the reduced nesting level in the file making it less likely that learners will get lost in the indentation.

For consistency purposes, I'd recommend sticking with the old format until you make all of your code follow the new style at once, then switching over to the file-scoped option. However, I would not make this anything more than a refactoring suggestion.

Prefer auto properties

Auto-properties are a wonderful thing in dotnet that saved everyone a lot of time when they came around early on in C#'s history.

While properties are one of the more difficult aspects of learning the basics of dotnet classes, auto properties make for brief, readable, and maintainable code and also prevent common mistakes.

I frequently see new learners try to use manual properties incorrectly like the following:

public class Snack
{
   private string name;

   public string Name
   {
      get
      {
         return Name;
      }
      set
      {
         Name = value;
      }
   }
}

To a new learner, this code looks reasonable, but both the getter and setter for Name contain an infinite loop since both are using the name of the property instead of the name of the backing field.

New learners are going to find this and it really is a bit of a rite of passage, but encountering stack overflow errors repeatedly distracts from the other learning attempts they're making. Trust me: they will eventually encounter stack overflows without the language making it easy for them.

My policy is to always prefer auto-properties and suggest them to developers, but do not warn.

Prefer simple 'using' statements

C# now gives us a simple "inferred scope" using statement that doesn't need the { } scope notation.

However, I don't like this simplified using statement for governing resource disposal, because it simplifies the scope of the resource too much.

Using the old syntax, it was clear where the IDisposable would be disposed:

using (MyDisposableClass disposable = new MyDisposableClass())
{
   disposable.DoSomething();
}

The familiar { } scope makes the resource disposal easier for learners to understand, which helps illustrate the point of the using statement.

On the flipside, the using MyDisposableClass disposable = new MyDisposableClass() syntax looks too much like a variable declaration that a new learner might neglect the importance of the using keyword and omit it.

As a result, I recommend against simple using statements and warning the user when they are used.

Prefer 'System.HashCode' in 'GetHashCode'

Your new learners are likely not overriding GetHashCode, but if they do, the System.HashCode option is a great way of avoiding beginner hash code mistakes and should be preferred at a suggestion or warning level.

Prefer method group conversion

In scenarios where you can provide a lambda expression, there are times when you are just calling a method directly and passing along an argument. For example: myList.Select(item => CalculateValue(item)).

In these cases, the code can be simplified down instead to myList.Select(CalculateValue).

As an experienced developer, I like this. As a teacher of newer developers, I don't like this.

Newer developers are usually still getting used to lambda functions with "fat arrow" notation. When this is the case, repetition and following the same rules usually helps reinforce concepts.

Using method groups is a "special case" of a lambda expression and complicates that learning process. For this reason I recommend preferring lambda expressions, but do not recommend using anything more than a refactoring hint to reinforce this.

Prefer top-level statements

Top-level statements are a newer feature of C# and to my mind were actually built to support simplified scenarios for teaching C#.

As you might guess, I'm all in favor of supporting new people learning C#, so I agree with preferring top-level statements, but leaving this as a refactoring hint is fine.

Parentheses Preference

I am extremely opinionated when it comes to parentheses in code.

I see a high number of errors from new developers who misunderstand parentheses and do not include them when they should. This is particularly important when mixing && and || in the same line, because these never work the way a new developer's intuition says they ought to.

As a result, I advocate for always including parentheses for clarity at the warning level, because it really can be that much of an issue for new developers.

I am far more willing to tolerate excessive parentheses for added clarity or safety than I am willing to tolerate the bugs we see from not using parentheses.

Expression Preference

There are far too many things in expression preference to go over individually here, so here's my full list of settings:

Now, let's talk about some high-level themes and noteworthy recommendations.

I recommend not preferring the object or collection initializers - at least at first. Constructors are a confusing topic, but an important one. It is healthy for new learners to thing about initialization as going through the constructor when just starting out in C#.

Once a new learner is comfortable with constructors and constructor chaining, it's fine to expand into initializers. In fact, I'd say it's even important to do so because so much of the newer C# features are focusing on using object initializers. A recent example of this would be the new required keyword.

You'll also notice here that I am strongly against expression-bodied members for new learners due to their added syntactical complexity. I recently wrote at length on expression-bodied members and my opinions on learning there, so check that article out for more details.

Pattern Matching Preferences

The pattern matching syntax popping up more frequently in recent C# releases is nice, but should not be in a new developer's first exposure to C# code. As a result, I recommend avoiding it in beginner-friendly codebases.

Variable Preferences

I do not recommend including inlined variables for new developers due to the added complexity in reading the code and minimal benefit.

Null Checking

The null checking section deals with null coalescing (??) and null propagation (?) operators.

I am cautiously optimistic about their use, particularly null propagation, but they should be used with caution.

The one case I do not like null coalescing is in throwing ArgumentNullException on a parameter being null.

In my experience the myParam = myParam ?? throw new ArgumentNullException sytnax is more confusing for new developers than it is helpful, so I recommend against its usage here.

Using Preferences

I have no strong opinions on the using directive placement and am fine to leave it in its default placement.

Modifier Preferences

This is a small section dealing with local functions and the readonly keyword.

I find that readonly slightly confuses new learners for a short period of time and then they're fine with it, so I'm not opposed to its inclusion as long as the tooling is subtle about it.

static, on the other hand, almost always severely confuses new learners and I do not find the slight performance benefits you might get from static to be worth the productivity penalties it presents in sidetracking a new developer. You may disagree, and that's fine, but this is my personal preference from my observations teaching.

Parameter Preferences

The only option in parameter preferences involves hinting that unused parameters should be removed. I'm in favor of this because this can often help new learners find unused code or bugs lurking in their applications.

New Line Preferences

I don't have any strong preferences for new line placement that might differ from the standard C# coding conventions.

In general I find a lot of value in line breaks for comprehension for new developers.

Other Changes

There are a few other setting changes that might be worth discussing to help the new developer experience in Visual Studio 2022+.

Naming Changes

Inside of the Text Editor > C# > Code Style > Naming section, there are three options for class, interface, and member naming standards.

I recommend moving the severity of these issues up to warnings as many mistakes I see new learners make could have been prevented by following dotnet naming standards.

Inline Diagnostics

I would also consider pairing these changes with the new experimental inline diagnostics feature for C# code in Visual Studio to help learners see warnings in their code as they go.

See my article on inline diagnostics for more details on what that is and how to get started with it.

Final Thoughts and a Parting Gift

All told, I believe that these settings lead to a more beginner-friendly dotnet development experience.

What I have done in this article is highlight a set of settings for learners that I believe enhances the learning or onboarding journey for that particular developer. I believe that focusing new developers on these settings and codebases written in them will help them achieve success as a developer faster.

These settings are particularly written with individual learners or teams of learners in mind. I'm not necessarily saying that you and your organization should adopt these settings department-wide for the benefit of your teams, but they may inspire some form of organizational standards you adopt.


Finally, I have a parting gift for you all for being so patient with this long article.

Because configuring things can be tedious, I have a .editorconfig file available with my recommendations for beginner-friendly C# coding standards saved into the file.

If you download the .editorconfig file from its GitHub repository, you can add it to your projects and have these settings enforced across your team automatically. This means that developers on your team will not need to make any custom configuration changes and Visual Studio will guide them towards more beginner-friendly coding standards.


I realize that a lot of my advice here is going to be contentious or divisive, as most discussions on code style tend to be. I do not want to cause unnecessary strife, but my hope is that some of these ideas spark improvements in the beginner-friendliness of your code.

If you have other advice for folks or other opinions, please share them in the comments. If you know of other settings that might help new learners, I'd also love to hear them. I have plenty of opinions on ways we could make dotnet easier to get into and am passionate about growing and enriching our technical community.

Author

  • Matt Eland
    Microsoft MVP in AI, Author of "Refactoring with C#"

    Matt Eland is a software engineering leader and data scientist who has served as a senior engineer, software engineering manager, professional programming instructor, and has helped build enterprise-level software at a variety of organizations before distinguishing himself as a Microsoft MVP in Artificial Intelligence by using technology to accomplish ridiculous things in the name of science and teaching others. Matt makes it his job to learn new things and share them with others through articles, videos, and talks at user groups and conferences covering a wide range of topics from software architecture to programming topics to artificial intelligence and data science. Matt is a current data analytics master's student, an AI Specialist at Leading EDJE, is the author of "Refactoring with C#" and is creating a LinkedIn course and book on Computer Vision on Azure. Matt occasionally sleeps as well.

    View all posts

1 comment

Announcing "Refactoring with C#" October 28, 2023 - 3:32 pm

[…] Refactoring in the Enterprise talks about organizational challenges in refactoring, including getting buy-in and time to change code, dealing with larger scale refactorings, and enforcing code standards through EditorConfig files. […]

Reply

Leave a Reply

Related Content

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More