Fail Fast
Indent Hadouken
public object is_smth(string txt)
{
if(txt == "hello")
{
// Some custom long code
// that expands on 50 lines
// of code with some other if's
return smth;
}
return null; // throw new Exception ();
}
public object is_smth(string txt)
{
if(txt != "hello")
{
return null; // throw new Exception ();
}
// Some custom long code
// that expands on 50 lines
// of code with some other if's
return smth;
}
zamieniamy
na
public static int DoSomething(string val)
{
int multiplier;
if(!int.TryParse(val, out multiplier))
{
return generate_code(true);
}
// do some processing
return generate_code(false);
int generate_code(bool isNull)
{
return isNull ? 0 : multiplier * 10;
}
}
Local Function
dostępne od C# 7.0
public static int DoSomething(string val)
{
int multiplier;
if(!int.TryParse(val, out multiplier))
{
return generate_code(true);
}
// do some processing
return generate_code(false);
static int generate_code(bool isNull)
{
return isNull ? 0 : 10;
}
}
Static Local Function
dostępne od C# 8.0
public static int DoSomething(string val)
{
if(!int.TryParse(val, out int multiplier))
{
return generate_code(true);
}
return generate_code(false);
int generate_code(bool isNull)
{
return isNull ? 0 : multiplier * 10;
}
}
Out Variables
dostępne od C# 7.0
public static int DoSomething(string val)
{
if(!int.TryParse(val, out _))
{
return generate_code(true);
}
return generate_code(false);
static int generate_code(bool isNull)
{
return isNull ? 0 : 10;
}
}
Null check
dostępne od C# 9.0
public static int DoSomething(string val!)
{
if(!int.TryParse(val, out _))
{
return generate_code(true);
}
return generate_code(false);
static int generate_code(bool isNull)
{
return isNull ? 0 : 10;
}
}
// old C# prior 6
public class Person
{
public int Age { get; private set; }
public Person(int age)
{
Age = age;
}
}
Read-only auto properties with initialization
dostępne od C# 6.0
// new C# 6 and up
public class Person
{
// is this the same???
public int Age { get; } = -1;
public Person(int age)
{
Age = age;
}
}
// old C# prior 6 with expression body changes from C# 7
public class Person
{
private readonly _age = -1;
public int Age { get => _age; }
public Person(int age)
{
_age = age;
}
}
Read-only auto properties with initialization
dostępne od C# 6.0
// new C# 6 and up
public class Person
{
public int Age { get; } = -1;
public Person(int age)
{
Age = age;
}
}
init read-only auto properties
dostępne od C# 9.0
// new C# 6 and up
public class Person
{
public int Age { get; init; } = -1; // explicit init!
public Person(int age)
{
Age = age;
}
}
var p = new Person(10);
Person pp = new (10);
var ppp = new Person
{
Age = 10
};
expression body
dostępne od C# 7.0
public class Person
{
public string FullName { get; }
// bit too implicit
public Person(string fullName) => FullName = fullName;
}
record
dostępne od C# 9.0
// old C#
public class Person
{
public string FullName { get; }
// bit too implicit
public Person(string fullName) => FullName = fullName;
}
# new C#
data public class Person(string FullName);
data public class Person { string FullName; }
filters/guards on Exception
dostępne od C# 6.0
try
{
}
catch(HttpRequestException hre) when (hre.Message.Contains("404"))
{
// do something for 404
}
catch(HttpRequestException hre) when (hre.Message.Contains("401"))
{
// do something for 401
}
catch(Exception ex)
{
}
in keyword
dostępne od C# 7.2
public class Person
{
public int Age { get; }
public Person(int age) => Age = age;
public bool IsOlder(in int age)
{
return Age > age;
}
}
var age = 18;
var p = new Person(10);
p.IsOlder(in age);
public int SomeAction(in int index, byte[] array)
{
var change = 0;
for(; change < 10; change++)
{
array[index + change] = change;
}
// will not work
// index += change;
return change;
}
touples / destructors
dostępne od C# 7 + 7.1
(string FirstName, string LastName, int Age) person = ("Jan", "Kowalski", 33);
var info = $"{person.FirstName} {person.LastName} is {person.Age} old";
var person = (Name: "Jan", Surname: "Kowalski");
// person.Name
int age = 5;
string name = "Jan";
var pair = (age, name);
// pair.age, pair.name
var point = new Point(3, 4);
(int X, int Y) = point;
(_, int Y) = point;
public class Point
{
public Point(int x, int y) => (X, Y) = (x, y);
public int X { get; }
public int Y { get; }
public void Deconstruct(out int x, out int y)
{
(x, y) = (X, Y);
}
}
using declaration
dostępne od C# 8
// old C#
using(var reader = new StringReader(str))
{
var line = reader.ReadLine();
return line;
}
// new C#
using var reader = new StringReader(str);
var line = reader.ReadLine();
return line;
// or we can even using await if object implements IAsyncDisposable
await using var obj = new DisposableObject(str);
var result = reader.DoAction();
return result;
pattern matching
dostępne od C# 7.0
int num = 10;
int? nullNum = null;
var numIsNum = num is int newNum;
var numIsAlwaysSomething = num is var anotherNuNum;
var nullNumIsNull = nullNum is null;
var nullNumIsNotNull = nullNum is {};
if(nullNum is int anotherVar)
{
}
// we don't need to do if(obj is Test) { var x = obj as Test; }
pattern matching
dostępne od C# 9.0
int num = 10;
int? nullNum = null;
var nullNumIsNull = nullNum is null;
// even more explicit
var nullNumIsNotNull = nullNum is not null;
// than
var nullNumIsNotNullOld = nullNum is {};
pattern matching
dostępne od C# 7.0 + 7.1 (Generic support)
public void Do<T>(T a)
{
switch(a)
{
case 0:
break;
case IEnumerable<ushort> myVar:
// do somthing with myVar;
break;
case int n when n > 0:
// do something with n
break;
case null:
default:
break;
}
}
pattern matching
dostępne od C# 9.0
public void Do<T>(T a)
{
switch(a)
{
case 0:
break;
case IEnumerable<ushort> myVar:
// do somthing with myVar;
break;
case int n when n > 0:
// do something with n
break;
case not null:
// do something
break;
case null:
default:
break;
}
}
number literals
dostępne od C# 7.0 + 7.2
// old C#
var i = 100000;
var x = 0xFFBB;
var b = 0b01011100;
// new, clear code over concise code
var i = 100_000;
var x = 0x_FF_BB;
var b = 0b_0101_1100;
null coalescing assignment
dostępne od C# 8.0
// old C#
List<int> list = null;
if(list == null)
{
list = new List<int>();
}
int? i = null;
i = i == null ? 10 : i;
i = i == null ? 20 : i;
// new C#
List<int> list = null;
list ??= new List<int>();
int? i = null;
// this might lead to miss understanding
i ??= 10;
i ??= 20;
switch expression
dostępne od C# 8.0
public static string DailyGreeting(DayOfTheWeek day)
{
return day switch
{
DayOfTheWeek.Monday => "It's the luckiest day of the weekly!",
DayOfTheWeek.Tuesday => "",
DayOfTheWeek.Wednesday => "It's hump day",
DayOfTheWeek.Thursday => "It's almost the weekend!",
DayOfTheWeek.Friday => "It's the weekend baby!",
DayOfTheWeek.Saturday => "Party like it's you're on spring break",
DayOfTheWeek.Sunday => "Lazy day...",
_ => throw new ArgumentException("invalid enum value", nameof(day))
};
}
public static decimal ComputeSalaryBooster(Address location, decimal baseSalary) =>
location switch
{
{ City: "Warsawa" } => baseSalary * 1.5M,
{ City: "Krakow" } => baseSalary * 1M,
{ City: "Wroclaw" } => baseSalary * 1.1M,
// other cases removed for brevity...
_ => 0M
};
switch expression
dostępne od C# 9.0
public static string DailyGreeting(int day)
{
// it might be hard to read for more complex code, but for simple
/ if else if it should work perfectly
return day switch
{
> 1 and < 2 => "It's the luckiest day of the weekly!",
< 4 => "",
5 => "It's the weekend baby!",
<= 7 => "Party like you're under sharp fog shadows",
_ => throw new ArgumentException("err", nameof(day))
};
}
touples with pattern matching
dostępne od C# 8
// from MSDN, using tuples
public static string RockPaperScissors(string first, string second)
=> (first, second) switch
{
("rock", "paper") => "rock is covered by paper. Paper wins.",
("rock", "scissors") => "rock breaks scissors. Rock wins.",
("paper", "rock") => "paper covers rock. Paper wins.",
("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
("scissors", "rock") => "scissors is broken by rock. Rock wins.",
("scissors", "paper") => "scissors cuts paper. Scissors wins.",
(_, _) => "tie"
};
static Quadrant GetQuadrant(Point point) => point switch
{
(0, 0) => Quadrant.Origin,
var (x, y) when x > 0 && y > 0 => Quadrant.One,
var (x, y) when x < 0 && y > 0 => Quadrant.Two,
var (x, y) when x < 0 && y < 0 => Quadrant.Three,
var (x, y) when x > 0 && y < 0 => Quadrant.Four,
var (_, _) => Quadrant.OnBorder,
_ => Quadrant.Unknown
};
default literal expression
dostępne od C# 7.1
// old C#
int i = default(int);
public Task SomeAsync(CancelletionToken ct = default(CancelletionToken)) {}
// new C#
int i = default;
public Task SomeAsync(CancelletionToken ct = default) {}
index and range
dostępne od C# 8
var words = new string[]
{
// index from start index from end
"Siala", // 0 ^9
"baba", // 1 ^8
"mak", // 2 ^7
"nie", // 3 ^6
"wiedziala", // 4 ^5
"jak", // 5 ^4
"a", // 6 ^3
"dziad", // 7 ^2
"wiedzial" // 8 ^1
}; // 9 (or words.Length) ^0
// last word
var last = words[^1];
var previousToLast = words[^2];
// last two words:
var dziadWiedzial = words[^2..^0];
// first four words:
var firstPhrase = words[..4]
// first, second and third word, without forth
Range phrase = 1..4;
var fromRange = words[phrase];
words[^2] = "nie";
words[^1] = "powiedzial";
nullable reference
dostępne od C# 8
// csproj
// <LangVersion>8.0</LangVersion>
// <Nullable>enable</Nullable>
//
// #nullable enable
// #nullable restore
// non-nullable reference type
string name;
// nullable reference type
string? nameBis;
// non-nullable reference type
Person person;
// nullable reference type
Person? personBis;
// we know that personBis is not null, so omit comipiler warning
personBis!.Name;
Person p = null; // warning
Person p = null!; // ok
Person p = default!; // ok
async main
dostępne od C# 7.1
namespace TestProduct
{
public class Program
{
static async Task<int> Main ()
{
var i = 10;
var z = static_local();
var t = new Test();
retrun Task.FromResult(1);
static int static_local() => return 1; // c# 8
}
}
}
public class Test {}
"main"
dostępne od C# 9
var i = 10;
var z = static_local();
var t = new Test();
retrun Task.FromResult(1);
static int static_local() => return 1;
public class Test {}
default interface implementation
dostępne od C# 8
public interface ILogger
{
void Info(string message);
void Error(string message) => Debug.WriteLine(message);
// New method
void Warn(string message)
{
Debug.WriteLine(message);
}
}
public class Logger : ILogger
{
public void Info(string message) => Debug.WriteLine(message);
}