Novinky v C# 6.0


Návrh třídy

Getter-only Auto Properties

Automaticky implementované vlastnosti nás zbavují nutnosti definovat private členy pro jednoduché “get; set;” situace. V C# 6.0 mohou být vlastnosti i read-only.

public class Customer
{
    public string First { get; }
    public string Last { get; }
}

Auto Properties Initializers

A když už máme getter automatický, bylo by hezké property rovnou i inicializovat. V C# 6.0 to jde intuitivně přímo v deklaraci.

public class Customer
{
    public string First { get; set; } = "Jane";
    public string Last { get; set; } = "Doe";
}

Expression-bodied members

C# 6.0 si půjčuje lambda syntaxi a dovoluje nám tyto metody zredukovat.

Z tohoto kódu:

class Point
{
    int x;
    int y;

    Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int X
    {
        get { return x; }
    }

    public int Y
    {
        get { return y; }
    }

    public double DistanceFromZero()
    {
        return Math.Sqrt(x*x*+y*y);
    }
}

Se tak stane:

class Point
{
    int x;
    int y;

    Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int X => x;
    public int Y => y;
    public double DistanceFromZero() => Math.Sqrt(x*x+y*y);
}

Tento postup funguje jak na metody, tak i na vlastnosti. C# v tomto případě pochopí, že nahrazujeme jednořádkový getter a schová implementaci na pozadí. Podmínkou tohoto zápisu je, že musí následovat pouze jeden příkaz. Jestliže se pokusíme použít složené závorky, a tedy napsat i více příkazů za sebe, kompilátor nahlásí chybu a program se nezkompiluje.

Práce se stringem

Nameof Operator

Nahrazuje proměnnou jejím názvem. Využijeme to například při generování JSONu nebo u bindingu, při kterém potřebujeme mít svázanou proměnnou ve třídě s výstupem, kde je proměnná uvedena v textové podobě. Kompilátoru to poté dovoluje automaticky nahradit název proměnné, aniž bychom jej museli ručně přepisovat.

class DemoClass
{
    public int MyVariable { get; set; }

    public override string ToString()
    {
        return string.Format("Class {0} have variable named {1}",nameof(DemoClass),nameof(MyVariable));
    }
}

String Interpolation

Nový způsob zápisu řetězce. Tento zápis plně nahrazuje string.Format() a poskytuje identickou funkcionalitu. Řetězec musí začínat znakem dolaru ještě před otevřením uvozovek. Místo indexu dáváme do složených závorek samotnou proměnnou, popřípadě kód, ze kterého se řetězec, který se má na zadané místo vložit, vypočte. Tuto funkcionalitu můžeme i vnořovat, například při použití LINQu. Následující ukázka nahrazuje ToString() metodu v předcházejícím příkladu a ukazuje vnoření.

public override string ToString()  
{  
    return $"Class {nameof(DemoClass)} have variable named {nameof(MyVariable)}";  
}
  
string[] World = { "Czech Republic","Europe","World"};  
string Prepared=$"Hello {World.Aggregate(((working,next) => $"{working} and {next}"))}";  
Console.WriteLine(Prepared); // Hello Czech Republic and Europe and World  

Zachytávání výjimek

Conditional Exceptions

Můžeme rozhodovat o zachycení výjimky nejenom na základě jejího typu, ale i na základě vnějšího stavu.

Syntaxe:

catch (ExceptionType e) when (expression)

Kde expression může být libovolný výraz, který vrací bool. Můžeme se rozhodovat na základě booleovské proměnné nad try blokem, operátorů porovnání nebo i na základě volání metody vracející bool. Nyní tedy můžeme mít různé catch bloky pro HttpException s různou odpovědí.

try
{
    throw new HttpException(404,"Error");
}
catch(HttpException e) when (e.GetHttpCode() == 401)
{
    Console.WriteLine("Unautorized");
}
catch(HttpException e) when (e.GetHttpCode() == 404)
{
    Console.WriteLine("Not found");
}
catch(HttpException e) when (e.GetHttpCode() == 500)
{
    Console.WriteLine("Internal server error");
}

Kromě filtrování výjimek se dá tato funkcionalita použít i k logování. Přitom nemusíme mít žádné vnořené try bloky, nemusíme znovu vyhazovat výjimku a podobné věci, které degradují výkon aplikace. Do podmínky dáme metodu, která bude výjimku logovat, ale bude vždy vracet false – catch blok nikdy neproběhne. Nebudeme si tím narušovat zásobník, ve kterém výjimka propadá, a budeme mít přesné informace, kde se výjimka vyskytla.

try
{
    // ...
}
catch(Exception e) when (Log(e))
{
}

private static bool Log(Exception e)
{
    Console.WriteLine($"Loged: {e.Message}");
    return false;
}

Await v catch a finally

V nové verzi C# 6.0 již můžeme použít asynchronní metody i v catch a finally blocích.

try
{
    // ...
}
catch (ArgumentException e) when (e.ParamName = nameof(human))
{
    // ...
    await LogAsync();
}
finally
{
    // ...
    await LogAsync();
}

Při použití asynchronních metod program pokračuje v kódu, který na asynchronní metody nečeká. Máme-li tedy metodu, která není asynchronní a která volá asynchronní metodu bez použití await, při prvním výskytu await bude program pokračovat ve volající metodě. Až skončí úloha, která blokovala asynchronní metodu, bude program opět pokračovat v asynchronní metodě, která teprve poté skončí. V tu chvíli ale už může být volající metoda dávno hotová. Pokud to převedeme na výjimky, může nastat situace, kdy program bude pokračovat, aniž by výjimka byla plně zachycena, protože blok catch nebo finally ještě není kompletní – čeká na dokončení asynchronní metody. V nové verzi už je tento případ ošetřen a můžeme použít asynchronní metody i v catch a finally blocích.

Další novinky

Null-conditional Operator

Zdrojáky jsou plné neustálého kontrolování, zda objekt, k němuž přistupujeme, není náhodou null. A když už není, tak ještě hlídáme patřičnou vlastnost, případně událost. C# 6.0 hromadu tohoto kódu odstraňuje pomocí operátoru ?..

Z tohoto kódu:

public static string GetThree(string value)
{
    string result = value;
    if (value != null)
    {
        result = value.Substring(0, Math.Min(value.Length, 3));
    }
    return result;
}

Se tak stane:

public static string GetThree(string value)
{
    return value?.Substring(0, Math.Min(value.Length, 3));
}

Kontrola, zda někdo odebírá event.

Z tohoto kódu:

public void OnPropertyChanged([CallerMemberName] string propName = "")
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}

Se tak stane:

public void OnPropertyChanged([CallerMemberName] string propName = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}

Static Members

Vedle klasického using, který importuje celý jmenný prostor, můžeme nově použít i using static, který importuje pouze statické metody konkrétní třídy. Using static importuje třídu, to ale neznamená, že můžeme vytvářet její instance. K dispozici nám budou pouze statické metody této třídy. Využitelné to je například pro třídu Console a Math, která se skládá pouze ze statických tříd.

using static System.Console;
using static System.Math;

class Program
{
    static void Main()
    {
        WriteLine(Sqrt(9));
    }
}

Index Initializers

Místo postupné inicializace prvků kolekce je možné zápis zjednodušit a iniciovat indexované objekty takto.

public Dictionary<string, int> Test()
{
    var result = new Dictionary<string, int>() {["A"] = X, ["B"] = Y};
    return result;
}

1 Response

  1. 25. 2. 2019

    […] 6.0 přišel s Expression-bodied members pro metory a read-only property. V C# 7.0 se tato funkcionalita rozšiřuje na konstruktory, […]

Napsat komentář

%d blogerům se to líbí: