To ensure the maintainability and consistency of code, it is essential to have a proper layout for class files from the beginning of a project. This helps prevent “spaghetti code” and gives developers a sense of stability. It is important to enforce this standard through code review and analysis tools. Otherwise, fixing it later can be expensive.
In this article, I will provide examples of how to format code classes properly. This formatting style has been refined over time and has been used in the Spargine repository, which you can view at https://bit.ly/Spargine. More detailed information can be found in my book, “Rock Your Code: Coding Standards for Microsoft .NET,” available on Amazon.
First, let’s start off with what should be at the top of every class file. A file, in most cases, should only contain one type.
At the top of every class file, should be a header, based on the StyleCop template, that contains the following information.
- Assembly name
- Original author name
- Last modified by name
- Last modified date
- Copyright information.
- Summary description
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.
Next, all the imported namespaces should be listed. I sort the System namespaces first. Only the namespaces in use should be listed. Type Ctrl-R, and Ctrl-G to keep them cleaned up and sorted.
If you use CodeRush, as I do, I let it keep them sorted and cleaned up when I save the file.
The namespace for the class should be listed next. In this example, I am using a new way of defining them by using a file-scoped namespace.
Next, let’s discuss the two parts of the class.
First is the documentation of the class. Describe the purpose of this class and what it does. Appropriate XML Documentation tags should be used. The more detail, the better. This is even more important later in the life of the project when a new developer might be working on the code.
To make documenting class files easy, I use GhostDoc. GhostDoc will write the documentation for you, if you use good coding standards, it will add all appropriate documentation tags and descriptions. The graphic above shows the default documentation that GhostDoc writes.
When defining a class, it’s important to keep a few key considerations in mind. Firstly, be sure to use the appropriate class modifier – most classes should be internal, with public reserved for those types used by other assemblies.
Additionally, most classes should be sealed, unless they are specifically intended for inheritance. When naming your class, choose a descriptive name and avoid acronyms unless they are well-known. If you are inheriting a class, consider including the base type name at the end of your type name for clarity.
Finally, when defining the class hierarchy, be sure to list the base type first followed by any interfaces the class implements. For more guidance on class design and inheritance, check out the Inheriting Classes section in my book “Rock Your Code: Coding Standards for Microsoft .NET”.
Constants and Fields
The next section of the class file should contain the definitions for constants, fields, and delegates.
To ensure clean and maintainable code, there are a few key rules to follow when defining constants and fields in your code.
Constants can be defined with any access modifier – private, internal, or public – depending on your specific use case. However, fields should always be private and never public, as this could break encapsulation and introduce issues.
When defining a field, be sure to include the readonly keyword if the object is new’d up as part of the definition or in the constructor. This ensures that the field cannot be reassigned later on, providing additional safety and consistency.
Finally, all fields should include XML documentation to provide clear and concise explanations of their purpose and behavior. By following these guidelines, you can write clean, reliable code that is easy to understand and maintain over time.
Next, the constructors for the type should be defined.
Constructors are an essential part of any class and play a critical role in initializing its state. To ensure that your constructors are effective and maintainable, there are several key rules to follow.
First, always use descriptive XML documentation that includes appropriate documentation tags. This documentation helps other developers understand the purpose of the constructor and how to use it correctly.
Second, use the appropriate modifier for the constructor based on your specific use case. In general, most constructors should be public, but there may be cases where a protected or private constructor is more appropriate.
Third, constructors should only be used to set field variables or new up objects. It is crucial not to call other code from a constructor, as this could introduce an Exception that could crash the entire application.
Finally, never throw an Exception from a constructor, as this could cause unexpected behavior and make your code more difficult to debug. Instead, consider using a factory method to handle any potential exceptions or error conditions.
By following these rules, you can ensure that your constructors are well-designed, easy to use, and free from potential bugs or errors.
Events and Delegates
Next, define any events or delegates. Always include XML documentation.
Next, let’s talk about the importance of properly formatting properties. In Object-Oriented Programming (OOP), it’s crucial to validate the data that is being passed into a property. Failure to do so breaks encapsulation and violates OOP principles. Unfortunately, this is often neglected in the code I review. The only time it’s acceptable to skip validation is when the type is a pass-through, meaning it’s just representing data that is not modified. In such cases, using the record type is a great choice for these classes.
When defining properties, it’s crucial to ensure that the data they represent is valid and consistent with the design. To achieve this, follow these guidelines:
- Use appropriate attributes to specify the behavior and characteristics of the property.
- Choose a descriptive and clear name for the property that avoids acronyms unless they’re widely known.
- The accessor of the property should generally be either public or internal.
- Avoid using an object as the return type, as this may lead to performance issues such as boxing.
- In the getter, only return the private field that the property represents. Do not call other code as this may affect performance.
- Ensure the property checks that the incoming value is not the same as the one already stored in the field. If it is, just return. Otherwise, set it to the field if it’s valid and appropriate, and fire off any necessary events.
- Validate the value coming into the property based on business rules. If invalid, throw an appropriate exception such as ArgumentOutOfRangeException.
- When accessing the property data within the class, always use the property instead of the field. This guarantees that the data is validated and any events are triggered.
The HasValue() method shown above is a Spargine extension method.
I will discuss the important parts of defining and validating the parameters for methods.
Below are some guidelines to follow when writing methods:
- Always use the appropriate access modifier for your method. Unless the method is being used by an outside assembly, avoid using the public access modifier.
- If your method doesn’t access any class data, mark it as static. This makes it easier to use and can improve performance slightly.
- When your method returns data (a function), use the appropriate type. Avoid using object, as this could introduce boxing and affect performance.
- If your method is marked as async, make sure the name ends in “Async”.
- Validate your method parameters. Most parameters should be checked. Note that this includes enumerations! You can use an extension method like “CheckIsDefined()” from Spargine to easily validate an enumeration. The only parameter type that doesn’t need to be validated is a Boolean.
By following these guidelines, you can ensure that your methods are efficient, well-organized, and easy to use.
Using Blank Lines to Organize Code
For as long as I can remember, I have been using single blank lines to organize sections of code. I don’t know about you, but I find code like this hard to read.
Since there aren’t any blank lines, all the code lines are smashed up against the line before it. Here is how I format code like this.
I will explain how I use blank lines to organize code.
When writing methods, it’s important to include validation code at the top. This code should either validate the data and return a default value or throw an exception. To validate the data, I use extension methods from Spargine. After the validation code, I always insert a blank line to improve readability.
In this specific method, I am downloading data from a URL. After making the call, I again insert a blank line for readability.
Next, I validate that the return data is not null. Again, I insert a blank line after the validation block to improve readability.
The next action is to convert the data to an object. I create a serializer and then read the stream, followed by another blank line. Finally, I flush the stream, again followed by a blank line.
To make the code more readable and easier to follow, I always insert a blank line between every action. If you’d like to see more examples of how I format code, check out the source for Spargine at this link: https://bit.ly/Spargine.
Documentation is an important aspect of coding. It’s recommended to always add clear and concise documentation for public and protected constructors, methods, and properties. In fact, it’s best to document everything you write! This is crucial because it will show up in IntelliSense and can help other developers understand your code better, even years down the line.
When naming types and their methods and properties, take the time to ensure they are easily understandable. Avoid using acronyms unless they are well-known, like “XML”. Changing names later can be a time-consuming and frustrating process, so it’s best to get it right from the start. And don’t forget to spell-check your code! There are tools like GhostDoc that can help you catch spelling mistakes. Many developers struggle with spelling, so it’s important to be mindful of this.
To keep your code organized, it’s best to follow StyleCop/Analyzer rules. If you use CodeRush, it’s easy to set this up. For more information on how to organize your classes, refer to the Element Order section of the Assembly Layout chapter in my coding standards book. Keeping your code organized will not only make it easier to read and navigate, but it will also help prevent errors and bugs down the line.
Properly formatting your classes is a crucial aspect of creating professional-looking code that’s easy to understand and maintain, not just for yourself but for the entire team. This is especially important for new team members who will need to familiarize themselves with the codebase.
While it may seem like a lot to keep in mind, formatting guidelines can become second nature with practice. Plus, there are helpful tools like GhostDoc and CodeRush (which are both free) that can assist with identifying and refactoring code that doesn’t adhere to the guidelines I mentioned.
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.
One thought on “Code Quality: Formatting Classes in Microsoft .NET to Make Them Easy to Read and Modify”
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?