├── .editorconfig ├── Example.cs └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is the top-most EditorConfig file 2 | root = true 3 | 4 | # All Files 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | 9 | [*.cs] 10 | # Avoid using it. 11 | dotnet_style_qualification_for_field = false:warning 12 | dotnet_style_qualification_for_property = false:warning 13 | dotnet_style_qualification_for_method = false:warning 14 | dotnet_style_qualification_for_event = false:warning 15 | 16 | # Allows to use keyword instead of full class name. For example: use int instead of Int32. 17 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 18 | dotnet_style_predefined_type_for_member_access = true:suggestion 19 | 20 | # About modifiers and their order 21 | dotnet_style_require_accessibility_modifiers = always:warning 22 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async 23 | dotnet_style_readonly_field = true:suggestion 24 | 25 | # Parantheses preferences "a + b * c vs a + (b * c)" 26 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion 27 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion 28 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion 29 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion 30 | 31 | # Initialization 32 | dotnet_style_object_initializer = true:suggestion 33 | dotnet_style_collection_initializer = true:suggestion 34 | dotnet_style_explicit_tuple_names = true:suggestion 35 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 36 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 37 | dotnet_style_prefer_auto_properties = true:suggestion 38 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 39 | 40 | # Condition expression 41 | dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion 42 | dotnet_style_prefer_conditional_expression_over_return = true:suggestion 43 | 44 | # Null-checking preferences 45 | dotnet_style_coalesce_expression = true:suggestion 46 | dotnet_style_null_propagation = true:suggestion 47 | 48 | csharp_new_line_before_open_brace = all 49 | -------------------------------------------------------------------------------- /Example.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using LibraryNamespace; 6 | using LibraryNamespace; 7 | using OurNamespace; 8 | using OurNamespace; 9 | 10 | namespace Mandatory.Namespace 11 | { 12 | /* 13 | * Static members example 14 | */ 15 | public static class StaticExample 16 | { 17 | public const string Const2 = "Const2"; 18 | private const string Const1 = "Const1"; 19 | 20 | public static string publicStaticField; 21 | private static string _privateStaticField; 22 | 23 | static StaticExample() 24 | { 25 | PrivateStaticField = Const1 + Const1.GetHashCode(); 26 | } 27 | 28 | public static string StaticProperty { get; set; } 29 | 30 | public static string PublicStaticMethod(string argument) => PrivateStaticMethod(argument); 31 | 32 | private static string PrivateStaticMethod(string argument) => argument + Const1; 33 | } 34 | 35 | public static class ExampleExtensions 36 | { 37 | public static T[] AsArray(this T value) => new[] { value }; 38 | 39 | public static IEnumerable CreateString(this IEnumerable enumerable) 40 | => enumerable.Select(val => val.ToString()) 41 | .Aggregate((sum, val) => sum + " " + val) 42 | .AsArray(); 43 | } 44 | 45 | /* 46 | * Methods example 47 | */ 48 | public class MethodsExample 49 | { 50 | public string Name { get; set; } 51 | 52 | public IEnumerator YieldExample(T[,] valuesGrid, (int x, int y) from, (int x, int y) to) 53 | { 54 | for (int i = from.x; i <= to.x; i++) 55 | for (int j = from.y; j <= to.y; j++) 56 | yield return valuesGrid[i, j]; 57 | } 58 | 59 | public void ComplexMethod(IEnumerable arguments) where T : class 60 | { 61 | if (arguments == null) 62 | throw new NullReferenceException(); 63 | 64 | var parser = new Parser(); 65 | var exceptions = new List(); 66 | 67 | foreach (var value in arguments) 68 | if (value != null) 69 | { 70 | StaticExample.PublicStaticMethod($"{this}.{nameof(LongMethod)} - {item}"); 71 | 72 | if (!ValidateExample(value, parser)) 73 | exceptions.Add(new Exception($"{value} - is not valid")); 74 | } 75 | else 76 | throw new NullReferenceException("One of arguments is null"); 77 | 78 | if (exceptions.Any()) 79 | throw new AggregateException(exceptions); 80 | } 81 | 82 | private bool ValidateExample(string value, ParserExample parser) 83 | { 84 | using (var stream = new Stream()) 85 | using (var reader = new StreamReader(stream)) 86 | { 87 | try 88 | { 89 | parser.Parse(reader); 90 | return true; 91 | } 92 | catch (Exception e) 93 | { 94 | StaticExample.PublicStaticMethod(e.Message); 95 | return false; 96 | } 97 | } 98 | } 99 | 100 | public void MultilineStatemenstExample() 101 | { 102 | var value = "line1" 103 | + "line2" 104 | + "line3"; 105 | 106 | var value = IsValue1 107 | && IsValue2 108 | || IsValue3; 109 | 110 | var value = someObject.ChainedMethod1() 111 | ?.ChainedMethod2() 112 | ?.ChainedMethod3() 113 | ?? FallbackValue; 114 | } 115 | 116 | public void LongArgsMethod( 117 | StructExample firstLongArgument, 118 | EnumExample secondLongArgument, 119 | FlagsExample thirdLongargument 120 | ) 121 | { 122 | EmptyMethod(); 123 | EmptyMethod(); 124 | } 125 | 126 | public void LambdaMethod() 127 | => CallBackMethod(() => EmptyMethod()); 128 | 129 | public void LambdaMethod() 130 | => CallBackMethod(() => 131 | { 132 | for (int i = 0; i <= 10; i++) 133 | EmptyMethod(); 134 | }); 135 | 136 | public void CallBackMethod(Action action) => action(); 137 | 138 | public void EmptyMethod() { } 139 | 140 | public override string ToString() => $"[{nameof(MethodsExample)}]{Name}"; 141 | 142 | public class ParserExample 143 | { 144 | public T Parse(TextReader reader) => throw new NotImplementedException(); 145 | } 146 | } 147 | 148 | /* 149 | * Complex type declaration example 150 | */ 151 | public class BaseClass { } 152 | public interface ISomeInterface { } 153 | public interface IAnotherInterface { } 154 | 155 | public class ComplexType : BaseClass, ISomeInterface, IAnotherInterface 156 | where TArg1 : IEnumerable 157 | where TArg2 : class 158 | where TArg3 : TArg2 159 | { 160 | 161 | } 162 | 163 | /* 164 | * MonoBehaviour example 165 | */ 166 | [RequireComponent(typeof(Rigidbody))] 167 | public sealed class ComponentExample : MonoBehaviour 168 | { 169 | [Range(0, 10)] 170 | [SerializeField] 171 | private float _inspectorValue; 172 | 173 | private Rigidbody _rigidbody; 174 | 175 | public float InspectorValue 176 | { 177 | get => _inspectorValue; 178 | set => _inspectorValue = value; 179 | } 180 | 181 | private void Awake() 182 | { 183 | _rigidbody = GetComponent(); 184 | } 185 | 186 | private void Start() 187 | { 188 | 189 | } 190 | 191 | private void Update() 192 | { 193 | 194 | } 195 | } 196 | 197 | /* 198 | * Struct example 199 | */ 200 | public struct StructExample 201 | { 202 | public int IntValue { get; private set; } 203 | public string StringValue { get; private set; } 204 | 205 | public StructExample(int intValue, string stringValue) : this() 206 | => (IntValue, StringValue) = (intValue, stringValue); 207 | 208 | public static StructExample CreateStructExample(int intValue, string stringValue) 209 | => new StructExample() 210 | { 211 | IntValue = intValue, 212 | StringValue = stringValue 213 | }; 214 | } 215 | 216 | /* 217 | * Enums examples 218 | */ 219 | public enum EnumExample 220 | { 221 | ValueOne, 222 | ValueTwo, 223 | ValueThree 224 | } 225 | 226 | [Flags] 227 | public enum FlagsExample 228 | { 229 | None = 0x0000, 230 | Up = 0x1000, 231 | Down = 0x0100, 232 | Left = 0x0010, 233 | Right = 0x0001, 234 | Vertical = 0x1100, 235 | Horizontal = 0x0011, 236 | All = 0x1111 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C# code style 2 | 3 | ## SOME GENERAL RULES (mandatory) 4 | 5 | ### Naming: 6 | - `camelCase` for fields and arguments 7 | - `PascalCase` for types and methods 8 | - Avoid contractions and acronyms if it's not common or well-known (e.g. `Html`, `Jpeg`) 9 | - Identifiers should be meaningful (e.g. `vertexArray` or `loadedPage`, no `abc`, `number`) 10 | - Names: 11 | - Noun for types, fields and properties (e.g. `CarDescription`, `humanNames`, `MeatBicycle`) 12 | - Verb for methods (e.g. `Initialize`, `UpdateState`, `MakeGood`) 13 | - Question for bool fields, properties and methods (`isDisposed`, `ShouldUpdate`, `AreValid`) 14 | - Conventions: 15 | - `camelCase` for public and protected fields, variables and method arguments 16 | - `_camelCase` for private and internal fields 17 | - `PascalCase` for types, methods, properties and constants 18 | - `PascalCaseAsync` for `async` methods 19 | - `OnPascalCase` for events 20 | - `PascalCaseExtensions` for extension classes 21 | - `IPascalCase` for interfaces 22 | - `T` or `TPascalCase` for generic arguments 23 | - `PascalCaseAttribute` for attributes 24 | 25 | ### Modifiers: 26 | 27 | - Always add access modifiers(`public`/`protected`/`internal`/`private`). Even to Unity event methods (e.g. `Awake()`, `Start()`, `Update()` etc.) 28 | - Use `readonly` where possible 29 | 30 | #### Modifiers order: 31 | 32 | [`public`/`protected`/`internal`/`private`] ?[`static`] ?[`readonly`/`const`/`override`/`new`] 33 | 34 | ### Type members order: 35 | 36 | 1. Constants -> Static -> Not static -> Nested types 37 | 2. Fields -> Properties -> Events -> Constructors -> Methods 38 | 3. `public` -> `protected` -> `internal` -> `private` 39 | 40 | ### Blocks and indentation: 41 | 42 | - Only spaces are used for indentation, one tab = 4 spaces 43 | - Blocks begin on a new line. Exception for single line block (e.g. `{ get; set; }`, `{ }` or `{ return 0; }`) 44 | ```csharp 45 | bool IsValid { get; set; } 46 | 47 | void DoNothing() { } 48 | 49 | void Initialize() 50 | { 51 | foreach (var element in collection) 52 | { 53 | ... 54 | } 55 | } 56 | ``` 57 | - Multi line statements should start with an indentation level and an operator on every line except first 58 | ```csharp 59 | var result = veryLongArgumentName1 60 | + veryLongArgumentName2 61 | + veryLongArgumentName3; 62 | 63 | var linqResult = someEnumerable.Select(...) 64 | .Where(...) 65 | .Aggregate(...) 66 | .ToArray(); 67 | ``` 68 | - Nested using statments should be on one indentation level 69 | ```csharp 70 | using (var stream = new Stream(path)) 71 | using (var reader = new StringReader(stream)) 72 | { 73 | ... 74 | } 75 | ``` 76 | - Long arguments list should be formatted on one line per argument next to method declaration/call 77 | ```csharp 78 | var Method( 79 | string arg1, 80 | string arg2, 81 | string arg 82 | ) 83 | { 84 | ... 85 | } 86 | 87 | Method( 88 | value1, 89 | value2, 90 | value3 91 | ); 92 | ``` 93 | ## Spaces 94 | - Should be no spaces: 95 | - before brackets on method declarartion/call 96 | - before `,` and `;` 97 | - before `:` in named arguments 98 | - before and after `.` and `?.` 99 | - before and after `?[`, `[` and `]` 100 | - before `?` for nullables (e.g. `int?`) 101 | - between any unary operator and argument (e.g. `++`, `!`, `(T)`) 102 | - after `(` and before `)` 103 | - after `{` and before `}` in interpolated strings 104 | - before and after `<` and `>` in generic types 105 | - before parentheses for `nameof(...)`, `typeof(...)` and `default(...)` 106 | - before and after indentation 107 | - at the end of the line 108 | - Should be space: 109 | - before single line blocks 110 | - before and after `{` and `}` in one line 111 | - before and after `=`, `=>`, `??` 112 | - before and after `:` and `where` in type declaration 113 | - before and after `?` and `:` in ternary expressions 114 | - before and after any binary operators 115 | - arithmetical operators (e.g. `+`, `*`) 116 | - logical operators (e.g. `&&`, `||`) 117 | - relational operators (e.g. `==`, `<`, `>=`) 118 | - type operators (`as`, `is`) 119 | - before and after `new` 120 | - before parentheses in control structures: `for (...)`, `foreach (...)`, `while (...)`, `if (...)`, `catch (...)`, `lock (...)` 121 | 122 | ### `using` namespaces order 123 | 124 | 1. `System.*` namespaces 125 | 2. External dependencies (e.g. `UnityEngine`, `Zenject`, `Newtonsoft.Json`); 126 | 3. Internal dependencies (e.g. `Phygitalism.*`, `{ProjectName}.*`) 127 | 128 | ### Misc: 129 | 130 | - Lines should be no longer than 120 symbols. Better less than 100 131 | - Always specify namespaces for declared types 132 | - Avoid `this.` unless absolutely necessary 133 | - Every attribute on its own line. Exception for argument attributes 134 | ```csharp 135 | [Attribute1] 136 | [Attribute2] 137 | void SomeMethod() { ... } 138 | 139 | void AnotherMethod([Attribute3] object argument) { ... } 140 | ``` 141 | - Use `nameof(...)` instead if `"..."` 142 | ```csharp 143 | class SomeType 144 | { 145 | string TypeName => nameof(SomeType); 146 | } 147 | ``` 148 | - Avoid code commenting. Exception for unclear code, that can't be understood without comments. 149 | 150 | ### Unity: 151 | - Use private fields with attribute `SerializeField` for serializable fields 152 | ``` 153 | [SerializeField] 154 | private GameObject _someValue; 155 | ``` 156 | - `PascalCase` for asset's names 157 | 158 | ## GENERAL ADVISES (not mandatory, but very desirable) 159 | 160 | - Use `var` instead of type in variable declarations. 161 | - Replace `public` fields should be replaced with properties 162 | - Use expression syntax in methods and properties 163 | ```csharp 164 | string SomeValue => "result"; 165 | 166 | string CreateString() 167 | => enumerable.Select( ... ) 168 | .ToString(); 169 | ``` 170 | - Use string interpolation to format strings 171 | ```csharp 172 | Console.WriteLine($"My name is {me.Name}"); 173 | ``` 174 | - Split long method call chains to one call per line 175 | - Do not use nested functions 176 | - Do not use `#region` 177 | - Conditional compilation should be an exception 178 | 179 | ### Inspired by: 180 | 181 | - https://www.dofactory.com/reference/csharp-coding-standards 182 | - https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md 183 | - https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide 184 | - https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines 185 | - https://referencesource.microsoft.com 186 | - https://github.com/Unity-Technologies/UnityCsReference 187 | --------------------------------------------------------------------------------