Jakub Gutkowski PRO
Dad, husband, blogger, developer, geek. Loves to play with novelties, learn new languages and libraries, avid conference attender, passionate about meeting new people, always willing to help.
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);
}
By Jakub Gutkowski
Przy tworzeniu projektów rzadko mamy możliwość zmiany wersji środowiska w trakcie trwania projektu. Czasami takie projekty mogą ciągnąć się latami. Kiedy wracamy do żywych okazuje się, że dzięki paru prostymi aktualizacjom języka które wyszły mogliśmy pisać pewne rzeczy szybciej, prościej i wydajniej. Czasami zaś, po prostu nie mamy możliwości poznania pewnych udogodnień, gdyż rzadko mamy okazję implementować kod, który z takiej nowości może skorzystać (i nawet jeżeli już implementujemy, to o niej nie wiemy ;)). Na prezentacji przejdziemy przez szereg ciekawych nowości z C# (z najnowszej i kilku ostatnich wersji) z którymi mogłeś jeszcze nie mieć styczności jak i postaramy się zajrzeć w przyszłość by zobaczyć co kolejne wersje C# mogą nam przynieść jak i jakie udogodnienia środowisko programistyczne dotnet dla nas szykuje.
Dad, husband, blogger, developer, geek. Loves to play with novelties, learn new languages and libraries, avid conference attender, passionate about meeting new people, always willing to help.