Skip to content

Latest commit

 

History

History
927 lines (697 loc) · 13.8 KB

CONTRIBUTING.md

File metadata and controls

927 lines (697 loc) · 13.8 KB

Contributing Guidelines

EasyCache includes more than cache provider. Therefore seperate packages by cache provider. For Example;

  • EasyCache.Memory
  • EasyCache.Redis
  • EasyCache.MemCache

if you implement new provider follow name pattern. (EasyCache.{ProviderName})

🏗 EasyCache.Core

EasyCache.Core includes base methods for cache operation. If the code you develop is basic for cache operations, this development should be in EasyCache.Core.

🏗 EasyCache.AspNetCore

EasyCache.AspNetCore depends AspNetCore. If the code you develop is depends AspNetCore, this development should be in EasyCache.AspNetCore


Anatomy of a Class

Ordering Rules

This docuemnt contains class members ordering rules. C# class member must be placed with right order to keep code qulity high!

The following ordering applies to this project:

By Member Type

(SA1201 & SA1203)

  • Constant Fields
  • Fields
  • Constructors
  • Finalizers (Destructors)
  • Delegates
  • Events
  • Enums
  • Interfaces (interface implementations)
  • Properties
  • Indexers
  • Methods
  • Structs
  • Classes

By Access Modifiers

(SA1202)

  • public
  • internal
  • protected internal
  • protected
  • private

Statics

(SA1204)

  • static
  • non-static

Readonlies

(SA1214 and SA1215)

  • readonly
  • non-readonly

Summary

StyleCrop rules are used in this project and this document was prepared according to StyleCrop Rules Documentation


Architecture

Helpers

This folder includes helper codes and extensions which is undependent from any context. They are generic helper codes.


Interfacfes

This folder includes all interfaces in project.


Concrete

This folder includes all implementation in project.


Dependencies

EasyCache.Core

Library Description Version
Microsoft.Extensions.DependencyInjection Extension of Dotnet v5.0.1

EasyCache.AspNetCore

Library Description Version
Microsoft.AspNetCore.Mvc.Abstractions Mvc Abstractions v2.2.0
Microsoft.AspNetCore.Mvc.Core ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux. v2.2.5

EasyCache.MemCache

Library Description Version
EnyimMemcachedCore A Memcached client for .NET Core. Available on Nuget v2.5.1

EasyCache.Memory

Library Description Version
Microsoft.Extensions.Caching.Memory Extension of Dotnet v5.0.0

EasyCache.Redis

Library Description Version
Microsoft.Extensions.Caching.StackExchangeRedis Extension of Dotnet v5.0.1

Code Styles

This document is created to guide your style of code.

First rule is:

You MUST follow .editorconfig file!

You can get .editorconfig file from here

Other rules is listed below:

this Preferences

Fields

Do not prefer this

// Prefer:
capacity = 0;

// Over:
this.capacity = 0;

Property

Prefer this

// Prefer:
this.Id = 0;

// Over:
Id = 0;

Method

Do not prefer this

// Prefer:
Display();

// Over:
this.Display();

Event Access

Prefer this.

// Prefer:
this.Elapsed += Handler;

// Over:
Elapsed += Handler;

Predefined type preferences

For locals, parameters and members

⚠ Prefer predefined type instead of framework type.

For example use int instead of Int32

private int _member;
static void M(int argument)
{
	int local;
}

For member access expressions

static void M(int argument)
{
	int local = int.MaxValue;
}

'var' preferences

For built-in types

Prefer explicit type

// Prefer:
int x = 5; // built-in types

// Over:
var x = 5; // built-in types

When variable type is apparent

Prefer var

var cojb = new C(); //type is apparent from assignment expression

Elsewhere

Prefer var

var f = this.Init(); //everywhere else

Code block preferences

Prefer braces

When on multiple lines

// Allow:
if (test) Console.WriteLine("Text");

// Allow:
if (test)
    Console.WriteLine("Text");

// Prefer:
if (test)
{
    Console.WriteLine("Text");
}

// Over:
if (test)
    Console.WriteLine(
		"Text");

Prefer Auto Properties

⚠ Yes

// Prefer:
public int Age { get; }

// Over:
private int age;
public int Age
{
  get
  {
     return age;
  }
}

Prefer Simple using Statement

Yes

// Prefer:
void Method()
{
  using var resource = GetResource();
  ProcessResource(resource);
}

// Prefer:
void Method()
{
	using (var resource = GetResource())
		ProcessResource(resource);
}

// Over:
void Method()
{
  using (var resource = GetResource())
  {
     ProcessResource(resource);
  }
}

Parantheses preferences

In aritmethic operators

* / % + - << >> & ^ |

Never if unnecessary

// Prefer:
var v = a + b * c;

// Over:
var v = a + (b * c);

In other binary operators

&& || ??

Always for clarity

// Prefer:
var v = a || (b && c);

// Over:
var v = a || b && c;

In relational operators

< > <= >= is as !=

Always for clarity

// Prefer:
var v = (a < b) == (c > b);

// Over:
var v = a < b == c > b;

In other operators

Never if unnecessary

// Prefer:
var v = a.b.Length;

// Over:
var v = (a.b).Length;

Expression preferences

Prefer object initializer

⚠ Yes

// Prefer:
var c = new Customer 
{
    Age = 21
};

// Over:
var c = new Customer();
c.Age = 21;

Prefer collection initializer

⚠ Yes

// Prefer:
var list = new List<int>
{
    1,
    2,
    3,
};

// Over:
var list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);

Prefer pattern matching over is with cast check

⚠ Yes

// Prefer:
if (o is int i)
{
}

// Over:
if (o is int)
{
    var i = (int)o;
}

Prefer pattern matching over as with null check

⚠ Yes

// Prefer:
if (o is string s)
{
}

// Over:
var s = o as string;
if(s != null)
{
}

Prefer conditional expression over if with assignments

ℹ Prefer Yes up to one single condition!

// Prefer:
string s = expr ? "hello" : "world";

// Over:
string s;
if(expr)
{
    s = "hello";
}
else 
{
    s = "world";
}

Prefer conditional expression over if with returns

ℹ Prefer Yes

// Prefer:
return expr ? "hello" : "world";

// Over:
if(expr)
{
    return "hello";
}
else 
{
    return "world";
}

Prefer explicit tuple name

ℹ Prefer Yes, because it's new feature since C# 7

// Prefer:
public (string name, int age) GetCustomer()
{

}

// Over:
public Tuple<string,int> GetCustomer()
{

}

// Prefer:
(string name, int age) customer = GetCustomer();
var name = customer.name;
var age = customer.age;

// Over:
(string name, int age) customer = GetCustomer();
var name = customer.Item1;
var age = customer.Item2;

Prefer simple default expression

ℹ Prefer Yes

// Prefer:
void DoWork(CancellationToken cancellationToken = default) { }

// Over:
void DoWork(CancellationToken cancellationToken = default(CancellationToken)) { }

Prefer inferred tuple element names

ℹ Prefer Yes

// Prefer:
var tuple = (age, name);

// Over:
var tuple = (age: age, name: name);

Prefer inferred anonymous type member names

ℹ Prefer Yes

// Prefer:
var anon = new { age, name };

// Over:
var anon = new { age = age, name = name };

Prefer local function over anonymous function

⚠ Yes

// Prefer:
int fibonacci(int n)
{
    return n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}

// Over:
Func<int, int> fibonacci = null;
fibonacci = (int n) =>
{
    return n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2);
}

Prefer compound assignments

ℹ Prefer Yes

// Prefer:
value += 10;

// Over:
value = value + 10;

Prefer index operator

⚠ Yes

// Prefer:
var ch = value[^1];

// Over:
var ch = value[value.Length - 1];

Prefer range operator

⚠ Yes

// Prefer:
var sub = value[1..^1];

// Over:
var sub = value.Substring(1, value.Length - 2);

Use expression body for methods

ℹ Prefer when on single line

class Customer
{
    private int age;
    public int GetAge() => age;
}

Use expression body for constructors

❌ Never

class Customer
{
    private int age;
    public Customer(int age)
    {
        this.age = age;
    }
}

Use expression body for operators

ℹ When on single line

public static ComlexNumber operator +(ComlexNumber c1, ComlexNumber c2)
    => new ComlexNumber(c1.Real + c2.Real, c1.Imaginary + c2.Imaginary);

Use expression body for properties

ℹ When possible

class Customer
{
    private int _age;
    public int Age => _age;
}

Use expression body for indexers

ℹ When possible

class List<T>
{
    private T[] _values;
    public T this[int i] => _values[i];
}

Use expression body for accessors

⚠ When possible

class Customer
{
    private int _age;
    public int Age 
    {
        get => _age;
        set => _age = value;
    }
}

Use expression body for lambdas

When possible

Func<int, string> f = a => a.ToString();

Expression body for local functions

ℹ When possible

class Customer
{
    private int age;
    public int GetAge()
    {
        return GetAgeLocal();

        int GetAgeLocal() => age;
    }
}

Avoid unused value assignments

ℹ Discard

// Prefer:
_ = Computation(); // Unused value is explicitly assigned to discard
int x = 1;

// Over:
var unused = Computation(); // Unused value is explicitly assigned to an unused local
int x = 1;

// Over:
var x = Computation(); // Value assigned here is never used
int x = 1;

Avoid expression statements that implicitly ignore value

Discard

// Prefer:
_ = Computation();

// Over:
var unused = Computation();

// Over:
Computation();

Variable preferences

Prefer inlined variable declaration

ℹ Yes

// Prefer:
if (int.TryParse(value, out int i))
{
}

// Over:
int i;
if (int.TryParse(value, out i))
{
}

Prefer deconstructed variable declaration

ℹ Yes

// Prefer:
var (name, age) = GetPersonTuple();
Console.WriteLine($"{name} {age}");

(int x, int y) = GetPointTuple();
Console.WriteLine($"{x} {y}");

// Over:
var person = GetPersonTuple();
Console.WriteLine($"{person.name} {person.age}");

var point = GetPointTuple();
Console.WriteLine($"{point.x} {point.y}");

'null' checking

Prefer throw-expression

⚠ Yes

// Prefer:
this.s = s ?? throw new ArgumentNullException(nameof(s));

// Over:
if (s == null)
    throw new ArgumentNullException(nameof(s));

this.s = s;

Prefer conditional delegate call

⚠ Yes

// Prefer:
func?.Invoke(args);

// Over:
if (func != null)
{
    func(args);
}

Prefer coalesce expression

❌ YES!

// Prefer:
var v = x ?? y;

// Over:
var v = x != null ? x : y; // or
var v = x == null ? y : x;

Prefer null propagition

❌ YES!

// Prefer:
var v = o?.ToString();

// Over:
var v = o == null ? null : o.ToString(); // or
var v = o != null ? o.ToString() : null;

Prefer is null for reference equality checks

⚠ Yes

// Prefer:
if (value1 is null)
    return;

if (value2 is null)
    return;

// Over:
if (object.ReferenceEquals(value1, null))
    return;

if ((object)value2 == null)
    return;

'using' preferences

Prefered using directive placement

Outside namespace

// Prefer:
using System;
using System.Linq;

namespace Namespace
{
    class Customer
    {
    }
}

// Over:
namespace Namespace
{
    using System;
    using System.Linq;

    class Customer
    {
    }
}

Modifier preferences

Prefer readonly fields

⚠ Yes

// Prefer:
// '_value' can only be assigned in constructor
private readonly int _value = 0;

// Over:
// '_value' can be assigned anywhere
private int _value = 0;

Prefer static local functions

❌ YES!

If a local function doesn't use any member from method body, just declare it as static!

void Method()
{
    // Prefer:
    static int fibonacci(int n)
    {
        return n <= 1 ? n : fibonacci(n - 1) : fibonacci(n - 2);
    }
}

void Method()
{
    // Over:
    int fibonacci(int n)
    {
        return n <= 1 ? n : fibonacci(n - 1) : fibonacci(n - 2);
    }
}

Parameter preferences

Avoid unused parameters

ℹ All methods

// Prefer:
public void M()
{
}

// Over:
public void M(int param)
{
}

Naming Rules

This document is created to guide naming convensions in this project.

Naming Convensions

Context Format Samples
Resource Keys PascalCase
{Context}{SubContext}{Key}
UsersNotFound
AppSettingDialogError
Json Keys camelCase { "title": "Hello World!", "isEnabled": true, numberOfCup: 19 }
Endpont Routes camelCase
(Case In-Sensitive)
/v1/app/123123/userProfiles/987
Class PascalCase .Net Defaults
Variable camelCase .Net Defaults
Property PascalCase .Net Defaults
Method PascalCase .Net Defaults
Field camelCase .Net Defaults
Enum PascalCase .Net Defaults
Attribute PascalCase .Net Defaults
Constant UPPERCASE_UNDERSCORE .Net Defaults
Events PascalCase .Net Defaults