Overview
The general rule: “use Visual Studio defaults” . Using an IDE that supports the .editorconfig standard makes following these guidelines automatic.
An EditorConfig file (.editorconfig) is provided at the repository root for automatic C# formatting.
Core Principles
Brace Style
Allman Style (Correct)
K&R Style (Incorrect)
if ( condition )
{
DoSomething ();
}
if ( x > 0 )
{
Positive ();
}
else
{
Negative ();
}
Rule 1 : Use Allman style braces - each brace begins on a new line.
Single-Line Statement Blocks
Single-line statement blocks can omit braces, but must:
Be properly indented on its own line
Not be nested in other statement blocks with braces
if ( condition )
DoSomething ();
// Exception: nested using statements
using ( var resource1 = GetResource ())
using ( var resource2 = GetResource ())
{
UseResources ( resource1 , resource2 );
}
Indentation and Spacing
Rule 2 : Use four spaces for indentation (no tabs).
public class Example
{
public void Method ()
{
if ( condition )
{
DoSomething ();
}
}
}
Rule 7 : Avoid more than one empty line at any time.
Rule 8 : Avoid spurious free spaces.
Enable “View White Space” in Visual Studio (Ctrl+R, Ctrl+W ) to detect trailing spaces.
Naming Conventions
Fields
Rule 3 : Use _camelCase for internal and private fields with readonly where possible.
Instance Fields
Static Fields
Thread-Static Fields
Public Fields
private readonly string _name ;
private int _count ;
Field Type Prefix Example Private/Internal instance __camelCaseStatic s_s_camelCaseThread-static t_t_camelCasePublic none PascalCase
Visibility Modifiers
Rule 5 : Always specify visibility, even if it’s the default. Visibility should be the first modifier.
private string _foo ;
public abstract class MyClass ;
public static readonly int MaxValue = 100 ;
string _foo ; // Missing visibility
abstract public class MyClass ; // Wrong order
readonly static int MaxValue = 100 ; // Wrong order
Language Features
The this. Keyword
Rule 4 : Avoid this. unless absolutely necessary.
public void SetName ( string name )
{
_name = name ;
}
public void SetName ( string name )
{
this . name = name ; // Only if there's a public property called 'name'
}
The var Keyword
Rule 10 : Use var only when the type is explicitly named on the right-hand side.
var stream = new FileStream ( "file.txt" , FileMode . Open );
var count = ( int ) GetCount ();
var items = new List < string >();
Target-Typed new()
Rule 10 : Target-typed new() can only be used when type is explicitly named on the left-hand side in a definition statement.
FileStream stream = new ( "file.txt" , FileMode . Open );
List < string > items = new ();
stream = new ( "file.txt" , FileMode . Open ); // Type specified on previous line
Language Keywords vs BCL Types
Rule 11 : Use language keywords instead of BCL types.
int count = 0 ;
string name = "Ryujinx" ;
float value = 3.14f ;
int parsed = int . Parse ( "42" );
Int32 count = 0 ;
String name = "Ryujinx" ;
Single value = 3.14f ;
Int32 parsed = Int32 . Parse ( "42" );
Naming and Documentation
Method Names
Rule 13 : Use PascalCasing for all method names, including local functions.
public void ProcessData ()
{
void LocalHelper ( int value )
{
// Local function
}
LocalHelper ( 42 );
}
Constants
Rule 12 : Use PascalCasing for all constant variables and fields.
private const int MaxRetries = 3 ;
public const string DefaultName = "Ryujinx" ;
void Method ()
{
const int BufferSize = 1024 ;
}
Exception : Interop code constants should match the exact name and value of the external API.
nameof() Usage
Rule 14 : Use nameof(...) instead of "..." whenever possible.
if ( value == null )
throw new ArgumentNullException ( nameof ( value ));
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( Name )));
if ( value == null )
throw new ArgumentNullException ( "value" );
Code Organization
Namespace Imports
Rule 6 : Namespace imports should be at the top of the file, outside of namespace declarations.
using System ;
using System . Collections . Generic ;
using Ryujinx . Common ;
namespace Ryujinx . Graphics . Gpu . Shader
{
public class ShaderCache
{
}
}
Field Placement
Rule 15 : Fields should be specified at the top within type declarations.
public class Example
{
private readonly int _capacity ;
private static readonly string s_version = "1.0" ;
public Example ( int capacity )
{
_capacity = capacity ;
}
public void Process ()
{
}
}
Control Flow
Single-Statement if
Rule 18 : When using a single-statement if:
Never
Braces Required
Braces Optional
// Never use single-line form
if ( source == null ) throw new ArgumentNullException ( "source" );
// Required if any block uses braces
if ( x > 0 )
{
Positive ();
}
else
{
Negative ();
}
// Required if body spans multiple lines
if ( condition )
{
SomeMethod (
longParameter ,
anotherParameter );
}
// Can omit braces if all blocks are single-line
if ( x > 0 )
Positive ();
else if ( x < 0 )
Negative ();
else
Zero ();
Labels and goto
Rule 17 : When using labels, indent one less than the current indentation.
void Method ()
{
if ( condition )
{
retry :
if ( TryOperation ())
return ;
if ( ShouldRetry ())
goto retry ;
}
}
Special Rules
File-Specific Styles
Rule 9 : If a file differs from these guidelines, the existing style in that file takes precedence.
// If this file uses m_member instead of _member,
// continue using m_member for consistency
private int m_count ;
Type Design
Rule 19 : Make internal and private types static or sealed unless derivation is required.
internal sealed class HelperClass
{
}
private static class Constants
{
}
XML Documentation
Rule 20 : Use XML docs for:
Interfaces
Classes/methods with sufficient scope or complexity
/// < summary >
/// Memory cache of shader code.
/// </ summary >
class ShaderCache : IDisposable
{
/// < summary >
/// Default flags used on the shader translation process.
/// </ summary >
public const TranslationFlags DefaultFlags = TranslationFlags . DebugMode ;
}
Magic Numbers
Rule 21 : Define magic numbers as named constants.
private const int CurrentAge = 56 ;
private const int RetireAge = 68 ;
for ( int i = CurrentAge ; i < RetireAge ; i ++ )
{
}
for ( int i = 56 ; i < 68 ; i ++ ) // What do these numbers mean?
{
}
This may be ignored for trivial or syntactically common statements.
Non-ASCII Characters
Rule 16 : Use Unicode escape sequences (\uXXXX) instead of literal non-ASCII characters.
string copyright = " \u00A9 2024 Ryujinx" ;
string copyright = "© 2024 Ryujinx" ; // May get garbled
Complete Example
From src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs:
using Ryujinx . Common . Configuration ;
using Ryujinx . Common . Logging ;
using Ryujinx . Graphics . GAL ;
using Ryujinx . Graphics . Gpu . Engine . Threed ;
using Ryujinx . Graphics . Gpu . Engine . Types ;
using Ryujinx . Graphics . Gpu . Image ;
using Ryujinx . Graphics . Gpu . Memory ;
using Ryujinx . Graphics . Gpu . Shader . DiskCache ;
using Ryujinx . Graphics . Shader ;
using Ryujinx . Graphics . Shader . Translation ;
using System ;
using System . Collections . Generic ;
using System . IO ;
using System . Threading ;
namespace Ryujinx . Graphics . Gpu . Shader
{
/// < summary >
/// Memory cache of shader code.
/// </ summary >
class ShaderCache : IDisposable
{
/// < summary >
/// Default flags used on the shader translation process.
/// </ summary >
public const TranslationFlags DefaultFlags = TranslationFlags . DebugMode ;
private readonly struct TranslatedShader
{
public readonly CachedShaderStage Shader ;
public readonly ShaderProgram Program ;
public TranslatedShader ( CachedShaderStage shader , ShaderProgram program )
{
Shader = shader ;
Program = program ;
}
}
/// < summary >
/// Processes the queue of shaders that must save their binaries to the disk cache.
/// </ summary >
public void ProcessShaderCacheQueue ()
{
// Check to see if the binaries for previously compiled shaders are ready, and save them out.
while ( _programsToSaveQueue . TryPeek ( out ProgramToSave programToSave ))
{
ProgramLinkStatus result = programToSave . HostProgram . CheckProgramLink ( false );
if ( result != ProgramLinkStatus . Incomplete )
{
if ( result == ProgramLinkStatus . Success )
{
_cacheWriter . AddShader ( programToSave . CachedProgram , programToSave . BinaryCode ?? programToSave . HostProgram . GetBinary ());
}
_programsToSaveQueue . Dequeue ();
}
else
{
break ;
}
}
}
}
}
EditorConfig
The .editorconfig file at the repository root automatically enforces many of these rules in compatible IDEs.
Run before committing:
This tool automatically fixes style violations.
All PRs must pass dotnet format checks in CI. Run this locally before pushing.
IDE Support
IDE EditorConfig Support Visual Studio 2022+ Built-in Visual Studio Code Extension required JetBrains Rider Built-in Visual Studio for Mac Built-in
Next Steps
PR Guide Submit your first pull request
Building Build Ryujinx from source