This document serves as the official code standards guide development. Please note that this is a work in progress and may not encapsulate all standards expected of new or existing code. The included `.editorconfig` file can help enforce many of the standards mentioned below.
There are no requirements on a coding environment or IDE in order to contribute. If there are issues with a particular environment, please surface an issue with the project. If there are support files or updates needed to items such as the `.gitignore`, please open an issue or PR to assist the maintainers. Consider using an editor that can take advantage of the `.editorconfig` file to enable automatic enforcement.
Generally, the `System` and `SabreTools` libraries are going to be preferred over all external code. If external code is needed, reasonable alternatives need to be explored before it will be included. External code use can also be seen as an opportunity to include that functionality in a `SabreTools` project directly.
External code should first be considered in the form of a Nuget package to allow for easier versioning and tracking. If this is not viable, consult with a maintainer to see if including the code as a Git submodule or having a direct copy of the code included would be reasonable.
- Null-coalescing and null-checking operators can be used to make more readable statements and better get across what a statement or string of statements is doing.
-`#region` tags, including nested ones, can be used to both segment methods within a class and statements within a method. Indentation follows the surrounding code. These should be preferred over comments that indicate large blocks of related operations.
- Try to avoid use of preprocessor directives other than `#region`, `#endregion`, `#if`, `#elif`, and `#else` unless consulting ahead of time with the maintainers. Exceptions may be made for copied `#pragma` use, especially for known style violations.
## Naming Conventions
With the exception of directly-included external code, the following naming conventions should be observed:
- Methods and classes should use `PascalCase` for naming, even `internal` and `private` ones.
- Properties should use `PascalCase` for naming, even `protected` and `internal` ones.
- Fields should use `camelCase` with a `_` prefix for naming, even `protected`, `internal`, and `private` ones.
- Constants should use `PascalCase` for naming, even `protected`, `internal`, and `private` ones.
- In-method variables should use `camelCase` without a `_` prefix for naming.
- Avoid using `_` in the middle of any names that are included in library code. The exception to this rule are test methods where they are used to separate parts of the test description. e.g. `public void MethodName_NullInput_False() { ... }`
## Access Modifiers
Generally, all classes, methods, properties, and fields should include explicit access modifiers. This applies even if the assigned access modifier would be the same as the default value in C\#. Below are some other guidelines to be aware of around access:
- Avoid making everything `public`; only include the necessary level of access, including accounting for any tests that need to be written.
- Avoid making every method and class instance-based. Use `static` if your method does not need to access instance variables. Use `static` if your class only contains extensions or methods used by other classes.
using static SabreTools.Data.Models.BFPK.Constants;
using static SabreTools.Data.Models.PKZIP.Constants;
// Renamed usings in alphabetical order of local name
using CompressionMode = System.IO.Compression.CompressionMode;
using IOSeekOrigin = System.IO.SeekOrigin;
```
There should be no comments or additional newlines in between `using` statements. The newlines and comments in the above example are to help explain, rather than act as a direct guide.
There may be a need to version-gate some `using` statements if only certain .NET versions require particular code. If this is the case, all namespaces are still listed in the order above, with the preprocessor directives directly contained within. For example:
```csharp
using System;
#if NETCOREAPP
using System.Collections.Concurrent;
#else
using System.Collections.Generic;
#endif
using System.IO;
```
## Classes
Classes are considered to be logical groupings of functionality and should be treated as public-by-default. All non-private classes should be put into their own separate files. This includes typed-overrides, e.g. `Thing` and `Thing<T>`. Below are some other general guidelines for classes:
- Static classes are allowed, but they should not be the default case.
- Partial classes are allowed and are even recommended when there needs to be further logical breakdown of functionality. Common uses of partial classes include separating out interface implementations.
- If multiple interfaces are implemented, they should be listed in alphabetical order. If there are exceptions to this, they will be mentioned in project-specific developer guides.
- Private embedded classes are allowed, but should generally avoided if at all possible. This helps to both avoid discoverability issues as well as better help enforce separation of concern. If an embedded class is desired, talk to a maintainer before doing so.
## Methods
Methods should attempt to limited in size and scope wtihin reason. Methods that do too much are at risk of being untestable or cause maintenence issues. If a method has logical groupings of statements that could act as a standalone method by themselves, then consider doing so. Below are some other general guidelines for methods:
- Try to avoid including too much duplicate code across methods and classes. If you have duplicate code that spans more than ~5 lines, consider writing a helper method.
- Try to use expressive naming. e.g. use names like `PrintSectionTitles` and not `DoTheThing`.
- Use optional parameters when the default value is the most common. Method overloads are also acceptable to use here as well, depending on developer and maintainer preference.
There are multiple approaches to selections within code. There is often not a "best" solution, but the following guidelines should help guide toward a solution that both developers and maintiners can agree on.
- If comparing against values for assignment, try to use a `switch` expression instead. Please note that this may not always be possible depending on the types that are being compared.
- When using a `switch` statement, if all switch cases are single-expression, they can be written in-line. You can also add newlines between cases for segmentation or clarity. If a single case ends up overly-complex, consider separating it out into a new method and then calling that method instead.
- When using a `switch` expression, cases that lead to the same value can be combined using `or`. This is not required, especially if readability would be sacrificed.
Comments can be essential to the understanding of a given block of code. They can also be very helpful for figuring out appropriate use of the code and even sometimes help track down issues. Below are some other general guidelines for commenting:
- All classes and methods should contain a `summary` block at bare minimum to explain the purpose. For methods, it is highly recommended to also include `param` tags for each parameter and a `return` tag if the method returns a value. Do not hesitate to use `remarks` as well to include additional information.
- Comments should be expressive and fully explain what is being described. Try to avoid using slang or statements that may have double meanings. If you ware unsure if a comment will be read incorrectly, please consult with a maintainer.
- If comments include links, they can either be included as-is or using the `<see href="value"/>` tag. If the link is being included in a class- or method-level comment, prefer using the latter option.