This document describes the C# coding style of Layer7 Mobile SDK for Xamarin team. This guideline is recommended to comply with all C# implementations of our products.
Beyond the guidelines defined in this document, we also recommend reviewing the Microsoft C# Guide.
For more information, see developer website.
The Mobile SDK for Xamarin consists of multiple functional products separated into individual frameworks.
All of frameworks are recommended to comply with the coding guideline.
- Documentation
- Brace Style
- New Line
- Wrapping Lines
- Lambdas
- Spacing
- Enums
- #region directive
- using directive
- Nomenclature
- Comments
- File Structure
- Copyright Statement
- Error Handling
All code should be properly documented in .cs
files. Code documentation should adhere to Microsoft Doc's format.
Microsoft provides a documentation generation system based on XML comments. These comments are formally single line C♯ comments (indicated by triple slashes) containing XML tags. All comments must be placed directly before the code block to which the comments refer, for example:
/// <summary>
/// This is the class definition of CustomClass.
/// </summary>
public class CustomClass{}
A list of recommended tags for documentation comments can be found in Microsoft C# Programming Guide.
Visual Studio produces compiler-generated XML documents by taking the XML comments from the code and building them into an XML file. This XML file can then be used to generate human-readable documentation in a variety of forms including HTML, MSDN style documentation and IntelliSense within the code window.
Our team uses Monodoc as a tool to build HTML and IntelliSense documentation based on the compiler-generated XML files.
Recommended elements to document:
- Class description
- Property
- Enum type
- Constant
- Method
All braces get their own line (a C# convention):
For example:
class CustomClass
{
void DoSomething()
{
if (someTest)
{
// ...
}
else
{
// ...
}
}
void DoSomethingElse(int n)
{
for (int i = 0; i < n; ++i)
{
// ...
}
}
}
Not:
class CustomClass {
void DoSomething() {
if (someTest) {
// ...
} else {
// ...
}
}
void DoSomethingElse(int n)
{
for (int i = 0; i < n; ++i) {
// ...
}
}
}
Blank lines improve readability. They set off blocks of code which are logically related.
Two lines should always be used between:
- Logical sections of a source file
- Class and interface definitions
One line should always be used between:
- Methods
- Properties
- Local variables in a method and its first statement
- Logical sections inside a method to improve readability
Indent blank lines to make it easier to insert a statement.
When an expression will not fit on a single line, break it up according to these general principles:
- Break after a comma
- Break after an operator
- Prefer higher-level breaks to lower-level breaks
- Indentation for line wraps should use 4 spaces
When your argument list grows too long, split your method invocation across multiple lines. With the first argument on a new line after the opening parenthesis of the method invocation, and the closing parenthesis of the invocation on its own line at the same indentation level as the line with the opening parenthesis.
For example:
Console.WriteLine(
"Values of someObject: {0}, {1} and of anotherObject: {2} {3}",
someObject.FirstProperty,
someObject.SecondProperty,
anotherObject.FirstPart,
anotherObject.SecondPart
);
Not:
Console.WriteLine("Values of someObject: {0}, {1} and of anotherObject: {2} {3}", someObject.FirstProperty, someObject.SecondProperty, anotherObject.FirstPart, anotherObject.SecondPart);
Use the +
operator and break the string up into human-readable lines. The compiler will pick up that the strings are constant and concatenate them at compile time.
For example:
const string myVeryLongString =
"This is the opening paragraph of my long string. " +
"Which is split over multiple lines to improve code readability.";
Not:
const string myVeryLongString = "This is the opening paragraph of my long string. Which is split over multiple lines to improve code readability.";
In C#, a lambda expression is an anonymous function that allow you to write local functions that can be passed as arguments or returned as the value of function calls.
If your lambda takes a single argument, omit the parentheses around the argument list:
For example:
var admins = Users.Select (user => user.IsAdministrator);
Not:
var admins = Users.Select ((user) => user.IsAdministrator);
Whenever possible, omit types from lambda argument lists, and use simple names:
For example:
list.OnScroll += (sender, e) => {
// ...
};
Not:
list.OnScroll += (object sender, EventArgs e) => {
// ...
};
When the body of the lambda is a block, put the opening brace on the same line as the =>, indent the body of the block, and close the block at the same level of indentation as the line containing the opening brace:
For example:
people.ForEach (person => {
person.BrushTeeth ();
person.CallMom ();
person.RegisterToVote ();
});
button1.Click += async (sender, e) => {
await ExampleMethodAsync();
};
Not:
people.ForEach (person => {
person.BrushTeeth ();
person.CallMom ();
person.RegisterToVote ();
}
);
button1.Click += async (sender, e) => {
await ExampleMethodAsync();
};
Spacing is especially important in the code, as code needs to be easily readable by contributors.
- Use consistent spacing format across all places in the code
- Indentation should be done using spaces — never tabs
There should be exactly one blank line between methods to aid in visual clarity and organization. Whitespace within methods should separate functionality, but having too many sections in a method often means you should refactor into several methods.
Indent blocks with 4 spaces for optimal readability.
For example:
for (int i = 0; i < 10; i++)
{
Debug.Log("index=" + i);
}
Not:
for (int i = 0; i < 10; i++)
{
Debug.Log("index=" + i);
}
Declare automatic properties on a single line with a single spaces before and after {
, and the space before }
.
For example:
string SomeProperty { get; set; }
Not:
string SomeProperty{get;set;}
Complex properties go like this:
For example:
string SomeProperty
{
get
{
return someProperty;
}
set
{
someProperty = value;
}
}
Not:
string SomeProperty {
get {
return someProperty;
}
set {
someProperty = value;
}
}
Do not put a space before the left angle bracket in a generic type:
For example:
var someList = new List<int>();
Not:
var someList = new List <int>();
Do not put spaces inside parentheses, square brackets, or angle brackets:
For example:
Initialize(someObject);
object[i];
new List<int>();
Not:
Initialize( someObject );
object[ i ];
new List< int >();
Do use space to separate type parameters to generic types:
For example:
var someDictionary = new Dictionary<ObjectId, Object>();
Not:
var someDictionary = new Dictionary<ObjectId,Object>();
Do use single spaces in expressions liberally:
For example:
if (a + b > someMethod())
{
// ...
}
Not:
if (a+b>someMethod())
{
// ...
}
- Use singular names for enums.
- Do not explicitly specify a type of an enum or values of enums.
For example:
public enum Direction
{
North,
East,
South,
West
}
Not:
public enum Direction : long
{
North = 1,
East = 2,
South = 3,
West = 4
}
#region
is a preprocessor directive that lets you specify a block of code that you can expand or collapse when using the outlining feature of the Visual Studio Code Editor. It makes the code easier to organize and navigate by other developers.
Follow the Microsoft standard format of region directive.
For example:
#region Lifecycle methods
public async void SomeMethodAsync()
{
}
public async void AnotherMethodAsync()
{
}
#endregion
- Order of the
#region
should be identical in both interface and class files. - All of the methods under
#region
should be in alphabetical order.
using
directives may or may not be in alphabetical order- Always group using directives by common prefix, with shorter namespaces coming before longer ones
- Prefered, namespaces should be ordered in increasing order of platform specificity, with .NET namespaces first, followed by library or component namespaces, then Xamarin namespaces, finally application namespaces
For example:
using System;
using System.Xml;
using System.Collections.Generic;
using SomeLib;
using SomeLib.Extensions;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MyApp;
Not:
using MyApp;
using SomeLib.Extensions;
using MonoTouch.Foundation;
using System.Collections.Generic;
using System;
using System.Xml;
using MonoTouch.UIKit;
using SomeLib;
For naming, you should follow Microsoft Naming Guidelines.
Beyond the Microsoft naming conventions for general code development, there are special rules you should follow to ensure consistency and usability of the framework.
- All classes should start with the prefix,
MAS
, except for classes from other vendors. - Prefix interface names with the letter
I
, to indicate that the type is an interface. - Exactly one class per source file, although inner classes are encouraged where scoping appropriate.
Namespaces are all PascalCase, multiple words concatenated together, without hypens ( - ) or underscores ( _ ). We use the name of the framework as Namespace:
namespace MASFoundation
{
}
Write constants in PascalCase
, not UPPERCASE
.
For example:
public static const string VersionNumber = "1.1";
Not:
public static const string VERSION_NUMBER = "1.1";
Comments begin with //
followed by a single space, use sentence casing, and exhibit proper spelling and grammar. Use leading and trailing empty in-line comment.
For example:
//
// Place an in-line comment like this
//
Not:
//Don't do in-line comment like this
Long comments tend to grow from smaller ones, so it's simpler to always use //
than to switch to /* ... */
when a comment becomes "long".
For example:
//
// Programming style is a set of rules or guidelines used when writing the source code for a
// computer program. It is often claimed that following a particular programming style will
// help programmers read and understand source code conforming to the style, and help to
// avoid introducing errors.
//
// A classic work on the subject was The Elements of Programming Style, written in the
// 1970s, and illustrated with examples from the Fortran and PL/I languages prevalent at
// the time.
//
Not:
/*
* Programming style is a set of rules or guidelines used when writing the source code for a
* computer program. It is often claimed that following a particular programming style will
* help programmers read and understand source code conforming to the style, and help to
* avoid introducing errors.
*
* A classic work on the subject was The Elements of Programming Style, written in the
* 1970s, and illustrated with examples from the Fortran and PL/I languages prevalent at
* the time.
*/
Because we are maintaining multiple frameworks, ensure that all frameworks use the same style, format, and file structure.
File Header
Using Directives
Namespace Declaration
Type Declaration
Constants
Static Fields
Static Auto-Properties
Static Delegates
Static Events
Static Enums
Static Constructors
Static Complex Properties
Static Methods
Static Structs
Static Interfaces
Static Classes
Fields
Auto-Properties
Delegates
Events
Enums
Constructors
Finalizers (Destructors)
Complex Properties
Methods
Structs
Interfaces
Classes
The following copyright statement should be included at the top of every source file:
//
// Copyright (c) 2018 CA. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
//
All frameworks handle errors with proper framework error domains.
In Constants, or any framework constant class, properly define the error domain constants. The error domain format should be com.ca.FRAMEWORKNAME:SUB_DOMAIN.
- Do not write try-catch in all your methods. Use it only if there is a possibility that a specific exception may occur and it cannot be prevented by any other means
- Always catch only the specific exception, not generic exception
- In case of exceptions, give a friendly message to the user, but log the actual error with all possible details about the error
- Never do a 'catch exception and do nothing'. If you hide an exception, you will never know if the exception happened or not
For example:
try
{
// read from file.
}
catch (FileIOException ex)
{
// log error.
// re-throw exception depending on your case.
throw;
}
Not:
try
{
// read from file.
}
catch (Exception ex)
{
// Catching general exception is bad... we will never know whether
// it was a file error or some other error.
// Here you are hiding an exception.
// In this case no one will ever know that an exception happened.
return "";
}