Code Quality: Formatting Classes in Microsoft .NET to Make Them Easy to Read and Modify

To ensure the future success of the project, it is crucial to establish a well-organized structure for class files that promotes readability, maintainability, and consistency. This practice is essential in avoiding the creation of convoluted and error-prone code commonly referred to as “spaghetti code,” which can undermine the project’s stability. It is vital to implement these practices from the outset of the project and reinforce them through thorough code reviews and the use of code analysis tools. Delaying the adoption of this approach can lead to significant costs and complications in the future.

An enhanced edition of this article can be found in my book, “Rock Your Code: Coding Standards for Microsoft .NET,” now available on Amazon. This updated version comprises 230 new pages, providing valuable insights to help you and your team produce top-quality code that is easily modifiable.

Secure your copy by visiting the following link: https://bit.ly/CodingStandards8

In this article, I will provide a clear demonstration of how I format code classes, which I have refined and utilized throughout my career. While I cannot recall the specific moment I began adopting this approach, it has evolved naturally over time, resulting in a highly effective and intuitive formatting style.

To see more examples of how my class files are formatted, check out the Spargine repository at https://bit.ly/Spargine. The information in this article is from my book “Rock Your Code: Coding Standards for Microsoft .NET” available on Amazon. Let’s begin by discussing the essential components that should be included at the top of every class file. It is generally recommended that each file should only contain one type, ensuring a clear and concise structure. By adhering to this guideline, we enhance the readability and maintainability of our codebase.

Class Header

At the beginning of each class file, it is essential to include a header, following the StyleCop template, which provides vital information for documentation and maintenance purposes. This header should consist of the following details:

  • Assembly name: Specify the name of the assembly to which the class belongs.
  • Original author name: State the name of the original author who created the class.
  • Last modified by name: Indicate the name of the person who made the most recent modifications to the class.
  • Last modified date: Provide the date of the last modification made to the class.
  • Copyright information: Include appropriate copyright information to protect the intellectual property.
  • Summary description: Provide a concise summary description of the class’s purpose and functionality.

Including this header in each class file helps establish a consistent and informative documentation standard, facilitating easier understanding, collaboration, and maintenance of the codebase.

If you use GhostDoc (a free extension for Visual Studio), adding headers is very easy and easy to keep up to date. Simply choose “Document File Header” or “Document File” and the information will be added or updated.

Imported Namespaces

Following the class header, it is important to list all the imported namespaces. To maintain a clean and organized structure, I recommend sorting the System namespaces first. It is crucial to include only the namespaces that are used in the class file. To keep the imported namespaces cleaned up and sorted, you can use the shortcut Ctrl-R, Ctrl-G, which is available in most integrated development environments (IDEs). By regularly performing this cleanup, you ensure that the imported namespaces remain relevant and help minimize clutter in the code.

If you use CodeRush, as I do, I let it keep them sorted and cleaned up when I save the file.

Class Namespace

Following the imported namespaces, it is important to specify the namespace for the class. In this example, I employ a new approach by utilizing a file-scoped namespace.

namespace DotNetTips.Spargine.Core.Network;

Class Definition

Next, let’s discuss the two parts of the class.

The first crucial step is to document the class thoroughly. This documentation should describe the purpose of the class and provide a comprehensive understanding of its functionality. To achieve this, it is important to use appropriate XML Documentation tags to ensure compatibility with documentation generation tools.

By providing detailed documentation, we enable better comprehension of the class’s intended use and behavior. This becomes even more significant as the project progresses and new developers join the team. With comprehensive documentation in place, new team members can quickly understand the class and effectively contribute to its development and maintenance.

To simplify the process of documenting class files, I rely on GhostDoc. By adhering to good coding standards, GhostDoc automatically generates comprehensive documentation, including appropriate documentation tags and descriptions. The image above illustrates the default documentation generated by GhostDoc.

After documenting your code, it’s important to provide a clear definition of the class. Here are some key considerations to keep in mind:

  • Always use the appropriate class modifier. In most cases, the modifier should be internal rather than public. Public should only be used if the class needs to be accessed by other assemblies.
  • Most classes should be sealed by default. Only omit the sealed modifier if the class is specifically designed to be inherited (which is uncommon).
  • Choose a descriptive name for your class. Avoid using acronyms unless they are widely recognized and understood. If your class inherits from a base type, consider including the base type name at the end of the class name. For instance, if your class extends the Collection type, you could name it PersonCollection. For more detailed information on inheriting classes, refer to the Inheriting Classes section in the Class Design chapter of my book, “Rock Your Code: Coding Standards for Microsoft .NET.”
  • If your class inherits from a base type, specify the base type first in the class definition, followed by any interfaces that the class needs to implement.

Constants and Fields

The next section of the class file should include the definitions for constants, and fields. These elements provide important data and functionality for the class.

Here are the guidelines to follow when working with constants, fields, and documentation:

  • Constants: Constants can be defined with any access modifier: private, internal, or public, depending on their usage and intended scope. When defining constants, use meaningful names in uppercase letters to improve code readability.
  • Fields: Fields should always be declared as private, and only private. Avoid making them public, as this can break encapsulation and potentially introduce issues. If an object is instantiated as part of the field’s definition or in the constructor, consider using the readonly keyword to indicate that the field’s value cannot be modified once initialized.
  • Fields Documentation: It is good practice to include XML documentation for fields. XML documentation provides a standardized way to describe the purpose and usage of fields, making it easier for other developers to understand and use your code.

By adhering to these rules and guidelines, you ensure proper encapsulation, maintainability, and documentation for your constants and fields.

Constructors

The next step is to define the constructors for the type.  

Here are some rules to follow when working with constructors:

  • XML Documentation: Always use descriptive XML documentation for constructors, providing clear explanations of their purpose, parameters, and any exceptions they may throw. Utilize appropriate XML documentation tags to enhance the readability and understandability of the documentation.
  • Modifier Selection: Choose the appropriate modifier for the constructor based on the intended accessibility and usage. Common modifiers include public, internal, protected, and private, depending on the desired level of accessibility.
  • Purpose of Constructors: Constructors should primarily be used to set field variables or initialize objects within the class. Avoid calling other code or performing complex operations from within a constructor, as this could introduce exceptions that might crash the entire application.
  • Exception Handling: Never throw an exception directly from a constructor. Instead, consider using validation techniques or handling exceptions within the constructor’s logic to provide more graceful error handling.

By following these rules, you can ensure that your constructors are well-documented, appropriately accessible, and focus on their primary purpose of initializing objects and setting field variables.

Events and Delegates

Following the constructor definitions, you should proceed to define any events or delegates that are relevant to the class. These elements provide a way to handle and respond to specific actions or conditions.

Formatting Properties

Next, let’s discuss the importance of formatting properties. Validating the incoming data for a property is crucial to maintain encapsulation and adhere to the principles of Object-Oriented Programming (OOP). Unfortunately, in the code I review, I rarely come across this practice. The only exception to omitting validation is when the type serves as a pass-through, merely representing unmodified data. In such cases, utilizing the record type becomes an excellent choice for these classes.

Here are some guidelines for documenting and validating properties:

  • Include appropriate attributes.
  • Property accessors should, in most cases, be either public or internal.
  • Avoid using “object” as the return type to prevent potential performance issues associated with boxing.
  • Choose descriptive and easily understandable names for properties, avoiding acronyms unless they are widely recognized.
  • The property getter should only return the private field associated with it. Properties represent the state and should not call other code in the getter.
  • Properties should check if the incoming value is different from the one stored in the field. If they are the same, simply return without setting the value again to avoid potential performance issues. This becomes even more critical if the type throws events when values are changed.
  • Validate the incoming value based on business rules and throw the appropriate exception, such as ArgumentOutOfRangeException, if it is invalid.
  • Only if the value is valid should it be assigned to the field. After that, if applicable, fire off a “changed” event (not shown).
  • When accessing the property data within the class, always use the property itself, not the field. This ensures that the data is validated and any associated events are triggered.

The HasValue() method shown above is a Spargine extension method.

Method Formatting

In this discussion, I will emphasize the crucial aspects of defining and validating parameters for methods.

Here are guidelines for formatting your method:

  • Access Modifier: Always use the appropriate access modifier. Avoid using public unless the method is intended to be accessed by an external assembly.
  • Static Method: If the method does not access any class data, mark it as static. This simplifies its usage and may slightly improve performance.
  • Return Type: If the method returns data (a function), use the appropriate type. Avoid using object as it can introduce boxing, which negatively affects performance.
  • Naming Convention for Async Methods: If the method is marked as async, ensure that its name ends with Async.
  • Parameter Validation: Validate the method parameters. Most parameters should be checked for validity. This also includes enumerations. You can use the extension method CheckIsDefined() from Spargine to easily validate an enumeration, as shown in the code above.

By following these rules, you can ensure proper formatting and improve the overall quality of your methods.

Using Blank Lines to Organize Code

Throughout my programming experience, I have consistently relied on the practice of using single blank lines to organize different sections of code. However, the method shown below makes the code harder to read and comprehend.

Since there are no blank lines, all the code lines are compressed against the line before it. Here is the preferred way I format code for better readability and organization:

I will explain how I use blank lines to organize code.

  1. The first step in organizing methods is to include validation code at the top. This code should either validate the data and return a default value or throw an exception. In my case, I utilize extension methods in Spargine for data validation. Following the validation code, I insert a blank line for clarity.
  2. Moving on to the specific method, the subsequent action involves downloading data from a URL. After making the call, I insert a blank line to separate it from the following code.
  3. Once the data is retrieved, I proceed to validate that it is not null. After the validation block, I include another blank line to enhance visual separation.
  4. The next action is to convert the data into an object. Here’s the breakdown:
    1. First, I create the serializer and read the stream, followed by a blank line.
    1. Then, I flush the stream, adding a blank line afterwards.
    1. Lastly, I return the object.

To improve code readability, I ensure there is a blank line between each action. For more examples of how I format code, feel free to explore the source code of Spargine: https://bit.ly/Spargine.

General Tips

Always include well-written documentation for public and protected constructors, methods, and properties. Make sure the documentation is easy to understand and provides clear explanations. This documentation will appear in IntelliSense, helping other developers understand the code and making it easier to maintain in the future.

Take the time to carefully name types, methods, and properties. Choose names that are descriptive and easy to understand. Avoid using acronyms unless they are widely known and accepted, such as XML. Changing names later can be a tedious and time-consuming task, so it’s important to get them right from the beginning. Additionally, make sure the names are spelled correctly. Tools like GhostDoc can assist in fixing spelling mistakes, as many developers struggle with spelling (me included!).

Organize classes according to StyleCop/Analyzer rules. If you use CodeRush, you can easily set up the rules and ensure your classes adhere to the recommended organization. For more detailed information, refer to the “Element Order” section of the “Assembly Layout” chapter in my coding standards book. This will help maintain consistency and readability in your codebase.  

Summary

I strongly believe that adopting a professional approach to writing classes is beneficial, not only for individual developers but also for the entire team. It’s important to consider that anyone could potentially be reviewing the code, and well-organized classes contribute to better understanding. This is particularly crucial for new team members who may need to quickly grasp the codebase. Do you share this perspective?

While it may seem overwhelming at first, once you start consistently formatting classes in this manner, it will become second nature. Additionally, utilizing tools like GhostDoc and CodeRush (both of which are free) can greatly assist in identifying and refactoring code to adhere to the guidelines mentioned earlier. These extensions provide valuable support in maintaining code quality and consistency.

By adhering to consistent formatting practices, you can ensure that your code is more readable, maintainable, and easier to collaborate on with your team members. So, do you agree that it’s worth the effort to follow these guidelines?

I hope you will pick up a copy of “Rock Your Code: Coding Standards for Microsoft .NET” for more information. You can also use it for coding standards for your team!

For information about the CodeRush refactoring tool from DevExpress, go here: CodeRush: Free IDE Productivity Extension for Visual Studio | DevExpress

For information about GhostDoc from Submain, go here: SubMain / GhostDoc – Painless Help Documentation

For many more articles on code quality, please do here: Code Quality – dotNetTips.com (wordpress.com)

Please make a comment below. I’d love to hear from you.

Pick up any books by David McCarter by going to Amazon.com: http://bit.ly/RockYourCodeBooks

One-Time
Monthly
Yearly

Make a one-time donation

Make a monthly donation

Make a yearly donation

Choose an amount

$5.00
$15.00
$100.00
$5.00
$15.00
$100.00
$5.00
$15.00
$100.00

Or enter a custom amount

$

Your contribution is appreciated.

Your contribution is appreciated.

Your contribution is appreciated.

DonateDonate monthlyDonate yearly

If you liked this article, please buy David a cup of Coffee by going here: https://www.buymeacoffee.com/dotnetdave

© The information in this article is copywritten and cannot be preproduced in any way without express permission from David McCarter.

One thought on “Code Quality: Formatting Classes in Microsoft .NET to Make Them Easy to Read and Modify

  1. Having a consistent standard for formatting IS important. However, there can be many ways. For example, having the most likely to be needed immediately visible when opening a file. 99% of the time a developer on the team opening the file does not need to see the “header”; if it is desired to have that information in the file, having it as a footer can eliminate some scrolling steps.

    Similar as to order. Is a developer who is opening a file most likely to want to see a private field or a public member?

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.