Skip to content

Instantly share code, notes, and snippets.

@Gutek
Last active February 8, 2022 09:33
  • Star 18 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save Gutek/200c2d2eab5fd81886d6db9854be1a6a to your computer and use it in GitHub Desktop.
Short cheat sheet of changes in C# language from version 6 to 9

CS 6

read-only auto properties

Small help with immutable types...

// private readonly int _age;
// public int Age { get { return _age; } }
public int Age { get; }

public Person(int age) 
{
    Age = age;
}

Auto-prop initialization

same as above, its readonly. we can change collection elements but can't override Nicknames.

public IList<string> Nicknames { get; } = new List<string>();

Expression body

private int _age = 10;
public int Age => _age;
public string AgeFormatted => $"{Age:000}";
public int GetAge() => Age + 10;

Using static

using static System.String;

public string Formatted => Format("this is test {0}", 10);

// nested types without base class name

Null-conditional operators

// int? age;
// if person == null, age == null, otherwise age == person.Age
var age = person?.Age; 
var ageBis = person?.Age ?? 0;

GetType(age); // int?
GetType(ageBis); // int

static Type GetType<T>(T o)
{
    return typeof(T);
}

String interpolation

var name = "Jan";
var surname = "Kowalski";
var fullName = $"{name} {surname}";
// |Left   |  Right|
var padding = $"|{"Left",-7}|{"Right",7}|";

Filter/guards

For instance we can use to handle status code 404

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) 
{

}

nameof

we can refactor name, and its "string" value will change too

public Person(string name) 
{
    if(name == null) 
    {
        throw new ArgumentNullException(nameof(name));
    }
}

few others

static Task DoThings() { return Task.CompletedTask; };
Task.Run(DoThings); // will run, when previously it didn't
Task.Run(() => DoThings());

private Dictionary<int, string> messages = new Dictionary<int, string>
{
    { 404, "Page not Found"},
    { 302, "Page moved, but left a forwarding address."},
    { 500, "The web server can't come out to play today."}
};

// new
private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

CS 7

out variables

// if(int.TryParse("0001", out var num))
if(int.TryParse("0001", out int num))
{
    return num;
}


if(int.TryParse("test", out _)) 
{
    return "Works";
}
else 
{
    return "It's not a number";
}

touples

finally no more Item1, Item2, Item3, Item4, Item5...

(string FirstName, string LastName, int Age) person = ("Jan", "Kowalski", 33);
var info = $"{person.FirstName} {person.LastName} is {person.Age} old";

// unpacking/destructing
var point = new Point(3, 4);
(int X, int Y) = point;

discards

using _ in declaration, meaning we do not care about result, param etc.

don't use it as parameter name.

pattern matching

if (input is int count)
{
    sum += count;
}

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;
    }
}

ref returns

public static ref int Find(int[,] matrix, Func<int, bool> predicate) {}
ref var i = ref Find(...);
i = 10;
// we are updating value in metrix passed to Find method on specific index

local functions

public static int DoSomething(string val)
{
    if(val == null) {
        return generate_code(true);
    }

    // do some processing

    return generate_code(false);

    int generate_code(bool isNull) 
    {
        return isNull ? 10 : 0;
    }
}

expressions bodies updated

public class Person
{
    public string FullName { get; }

    public Person(string fullName) => FullName = fullName;
}


private string _fullName;
public string FullName
{
    get => _fullName;
    set => _fullName = value ?? "Jan Kowalski";
}

other updates

throw can be used in from expressions bodies or in condiational operatior.

ValueTask for performance critical async code that might be synchronously or cached. Task is reference type and allocate heap memory.

literals 0b0001_0000 or 0xFF_DD and 1_000_000

CS 7.1

async main

static async Task<int> Main () 
{

}

default literal expression

// public Task SomeAsync(CancelletionToken ct = default(CancelletionToken)) {}
public Task SomeAsync(CancelletionToken ct = default) {}
int i = default;
int? ni = default;

small imp to touples

var x = 10;
var y = 20;
var point = (x, y);
var namedPoint = (X: x, Y: y);

pattern matching for generit type parameters

public void Method<T>(T param)
{
    switch (param)
    {
        case A a:
            Console.WriteLine("A");
            break;
        case B b:
            Console.WriteLine("B");
            break;
    }
}

CS 7.2

in keyword

// index passed by ref, instead of copy (smaller mem footprint)
// but SomeAction is not allowed to change index
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;
}

readonly/immutable struct

readonly public struct Point3D
{
    public Point3D(double x, double y, double z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public double X { get; }
    public double Y { get; }
    public double Z { get; }
}

more about ref readonly

https://docs.microsoft.com/en-us/dotnet/csharp/write-safe-efficient-code

one more ref for conditional expressions

ref var r = ref (arr != null ? ref arr[0] : ref otherArr[0]);

// r is a reference to first element of arr or otherArr, chaning r will change value
// in arr or otherArr at index 0

small improvemnt to named arguments

if we have passing argument in proper location, we do not need to provide its name.

better value literals

var bin = 0b_0101_1111;
var hex = 0x_FF_DD_EE;

private protected

protected private only avaliable to derived classes in the same assembly.

protected internal only avaliable to derived classes or all others in the same assembly

CS 7.3

Mostly efficient safe code improvements. Like reassigning ref var to differnt location. Also more types support fixed statement, stackalloc allow array initializations, etc.

small improvements to c#

// tupples support == and !=

// backing field will have attached MyFieldAttribute
[field: MyFieldAttribute]
public int MyProp { get; set; }

// out extended to field initialization, property initialization, constructor initialization
var strings = new string[1];
var r = from s in strings
        select int.TryParse(s, out var i); 

// updates to overload resolution rules

CS 8

more patten matching nad matching expressions

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 ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.075M,
        { State: "MI" } => salePrice * 0.05M,
        // other cases removed for brevity...
        _ => 0M
    };

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"
    };  

using updated

using(var reader = new StringReader(str)) 
{
    var line = reader.ReadLine();
    return line;
}

using var reader = new StringReader(str);
var line = reader.ReadLine();
return line;

async using and streams

// IAsyncEnumerable<int> SomeSequence
await foreach(var number in SomeSequence()) 
{
    Console.WriteLine(number);
}

await using (var disposableObject = new DisposableObject())
{
    //...
}
await using var disposableObject = new DisposableObject();

nullable reference

we need to set:

<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>

this will change how reference types works and what warnings we will get. compiler use static anaylysis to detect nulls.

// 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

// more about attributes for static analysis
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/nullable-analysis
// plus attributes:
// pre-conditions: AllowNull / DisallowNull
// post-condition: NotNull / MaybeNull
// [return: MaybeNull]

// conditional post-condition: NotNullWhen / MayBeNullWhen / NotNullIfNotNull
// [NotNullWhen(returnValue: false)]

indexies

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]
Range phrase = 1..4;
var fromRange = words[phrase];

words[^2] = "nie";
words[^1] = "powiedzial";

Null-coalescing assignment

List<int> list = null;

// assign if null
list ??= new List<int>();
int? i = null;

i ??= 10;
i ??= 20;
// i == 10;

other improvements

Adding static local functions (in 7.0 only normal function could be creted in method).

disposable ref structs.

interoplation strings can be delcared $@"" or @$"".

stackalloc initialization expression can be used in nested expressions.

default interface implementation like => and method mody

CS 9

There is already a post about upcoming changes...

https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/

field notes

  • remove discards in swith expression pattern matching
  • switch expression on "logical" ifs like if(a > 10) else if (a <= 9) else
a switch 
{
    > 10 => aaa,
    <= 9 => bbb
    null =>
    not null =>
}
  • null and not null patterns
  • C# 9 removes public class Program and allow us to just write statements from Main without Main method
  • init properties
public string Prop { get; init; }
  • data keywoard
// for Person class take a loog at the end of this code block, no
// matter how you will specify it, following will work

// no type on new, but type in declaration.
Person p = new { FirstName = "Jan", LastName = "Kowalski" };
// var in declaration, but then type after new
var p = new Person { FirstName = "Jan", LastName = "Kowalski" };
var pp = p with 
{
    FirstName = "Krzysiek"
};
var ppp = pp with 
{
    FirstName = "Jan"
};

p equals ppp = true
p reference equals ppp = false

data class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

data class Person
{
    string FirstName;
    string LastName;
}

data class Person(string FirstName, string LastName);
  • ! for null check - throws ArgumentNullException
public void Test(string name!) {} 
  • override for type

  • BEST EVER function of C# 9 <3 :D

int? i = obj == null ? null : 10;
var b = false;
int? j = b ? 10 : null;
@lkurzyniec
Copy link

Person p = new { FirstName = "Jan", LastName = "Kowalski" };
Is also valid.

@Gutek
Copy link
Author

Gutek commented May 28, 2020

Person p = new { FirstName = "Jan", LastName = "Kowalski" };
Is also valid.

in 9? yes, i thought i have that... but I had on slides not here ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment