Skip to content

Coding Standards: .NET Types and Type Members

WithLithum edited this page Jan 18, 2023 · 3 revisions

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

General

✔️ It is RECOMMENDED to follow .NET Framework Design Guidelines article regarding this topic for whatever this document does not specify.

Types

Enums

❌ Enumerations SHOULD NOT end with the words "Flags" or "Flag".

❌ Enumeration SHALL NOT have a name that ends with the words "Enum" or "Enumeration".

❌ Enumerations SHALL NOT have a only value.

An enumeration having only one value is against the purpose of the managed enumerations.

❌ Enumerations SHALL NOT have reserved values for future use.

Values can be added managed enumerations without breaking any compatiblity by JUST simply add them. There is no need for reserved values.

❌ The System.Enum type MUST NOT be inherited directly.

Use the feature of your language for this, for example, the enum keyword of C#. By inherting System.Enum, you cannot define anything inside it; this class only provides methods for your enumeration to use, a few static methods related to and for the enumerations, and provide a basis for languages to implement the enumeration feature.

Interfaces

✔️ Interfaces MUST begin with the letter 'I'.

For example, IEnumerable. This rule still applies even if the name itself begins with the letter 'I'. For example, IItem.

Classes

Exceptions

❌ Exceptions SHOULD NOT devire from ApplicationException.

The ApplicationException is a legacy of an early concept of .NET Exceptions: exceptions in the CLR devires from SystemException and exceptions created by application devires from ApplicationException. However, this concept never worked, but ApplicationException was kept for backward compatbility.

See this article for more information.

❌ Try-catch blocks MUST NOT catch ApplicationException.

Any sort of ApplicationException, whether techinical or non-techinical, contains sensitive information or not, could be devired from ApplicationException (this means it is meaningless to catch this type). If you want to catch all exceptions, simply catch Exception.

Static Classes

❌ Static classes SHOULD NOT be used for main logic.

Instead, use Singleton for the core logic.

Records and Structures

❌ Records and structures SHOULD NOT be used in case of possiblity of regularily passed to 'object' variables, parameters, etc.

Doing so involves boxing and unboxing. Not only this operation is slow, it can negatively affect the heap, and thus performance.

❌ Records and structures SHOULD NOT be used to store anything that must be regularily modified.

For this purpose, use classes instead.

❌ Records and structures SHOULDD NOT be used to store large pieces of data.

Value types, like structures, are copied every time it is assigned to a different variable, etc.

❌ Parameters for one-line records MUST NOT have a name that is in camelCase.

The parameters for one-line records are actually property names.

Type members

✔️ Type members MUST be PascalCase unless specified otherwise by this document, or whenever interopablity or script integration requires so.

Events

✔️ Events MAY use generic version of the EventHandler type (EventHandler<T>) for custom arguments.

✔️ Events with the default event handler is RECOMMENDED to use (EventArgs.Empty) instead of creating a new EventArgs instance for events with no arguments.

✔️ Event handlers are RECOMMENDED to use sender for the sender of the event (the first parameter) and e for the arguments (the second parameter).

❌ Event invokers MAY NOT trigger an event by calling them like a method. An event without at least one handler listening is considered null by .NET, and triggering an event without any listener will result in NullReferenceException.

Instead, use Event?.Invoke(sender, args) with the Event replaced by the name of the event.

Properties

❌ Properties SHOULD NOT be set-only.

Set-only properties are confusing, as developers tend to except that a property is get-able by default. Whenever there is a case when a value cannot be access and can only be mutated, use a mutator method that begins with the word Set.

❌ Properties that only serves as a wrapper for a field without any custom behaviour are NOT RECOMMENDED.

Whenever a wrapper property should be replaced by auto-property, do it.

Methods

❌ Private, internal or otherwise non-public methods SHALL NOT use camelCase for any reason.

Despite Google recommending this, this is not a good practice. Use PascalCase just like every other methods.

Fields

✔️ Private fields SHALL be prefixed by an underscore (_).

❌ Fields MAY NOT use Hungarian Notation.

❌ Declaring public, non-readonly fields is NOT RECOMMENDED. Use a property instead.

Misc

Parameter names

✔️ Parameter names SHALL be in camelCase, regardless of the language.