Petr Kříž

C# 7.0 - Nové vlastnosti

Vylepšení čísel

Do sady prostředků sloužících k vyjadřování číselných hodnot přibyly věci, které můžou sloužit primárně k vytváření bitových map – binární literály (binary literals) a oddělovače číslic (digit separators).

Binární literály reprezentují číslo v binárním zápise a začínají prefixem 0b. Je tak možné nahradit doposud užívanou praxi méně přehledných bitových posunů (či dokonce decimálních čísel), např. 1 << 3 lze nyní zapsat jako 0b0001000.

S tím se pojí jedna nevýhoda – pokud je binární zápis příliš dlouhý, dá se jen těžko okamžitě spočítat, na kterém místě stojí která jednička či nula. Proto designéři C# přišli s dalším rozšířením číslicové syntaxe, oddělovačem číslic. Ten je reprezentován podtržítkem a v samotném čísle se může vyskytovat mezi dvěma číslicemi (ne tedy např. mezi číslicí a desetinnou čárkou), navíc z důvodů lepšího formátování v jakémkoli počtu. 123 456 789 lze tedy binárně zapsat v 32 bitech jako 0b0000_‭0111_0101_1011___1100_1101_0001_0101‬.


Local functions

Třídy bývají často zaházeny množstvím metod, které jsou třeba pouze pomocnými metodami jiných metod a naděje na jejich znovupoužitelnost je mizivá. Pro lepší orientaci proto byla přidána možnost definovat metody – tedy vlastně už funkce (vzhledem k tomu, že určujícím rysem metody je příslušnost ke své přímo nadřazené třídě). Místní funkci lze deklarovat na místě klasického statementu, tj. téměř všude. Vývojář tak má jednak lepší přehled o seznamu metod v třídě, a na místě, kde se daná metoda používá, okamžitě přesně vidí, jak se daná metoda vlastně chová.


Zkrácení zápisu

Možnost zkrácení zápisu se nabízí ve dvou případech, u modifikátoru parametrů metod "out" a u zápisu vyhození výjimky v případě hodnoty null. Pro modifikátor out byla v C# 6 zavedena možnost deklarovat proměnnou přímo na místě parametru volání metody, např. Method(out int i). Scope této proměnné byl pak stejný, jako kdyby byla deklarována těsně před voláním metody. C# 7 tuto možnost rozšiřuje o použití operátoru automatického vyvození typu, tj. operátoru var. Nyní je tak možné psát Method(out var i).

Druhé zkrácení zápisu lze použít, pokud chceme na jednom řádku buď získat instanciovaný objekt, nebo vyhodit výjimkou, pokud má jeho proměnná hodnotu null, tj. zápis:

var result = command.ExecuteScalar() as Int32?; if(result.HasValue) { return result.Value; } else { throw new Exception(); }

lze zkrátit na

return command.ExecuteScalar() as Int32? ?? throw new Exception();

Podobně lze výjimku vyhodit i v ternárním operátoru

(parts.Length > 0) ? parts[0] : throw new InvalidOperationException("No name!");

či jako tělo "expression bodied" metody.

public string GetLastName() => throw new NotImplementedException();

Tuples

Typ Tuple, neboli n-tice, byl zaveden v .NET Frameworku 4.0. Má schopnost asociovat proměnné až 8 různých typů, přičemž poslední typ je doporučen jako rozšiřovací – na jeho místo přijde další Tuple, pokud osm míst nestačí. K položkám se pak přistupuje přes vlastnosti Item1-Item7, poslední položka má název Rest. C# 7 používá tento typ (resp. jeho odlehčenou hodnotovu obdobu ValueTuple) na pozadí k tvorbě velmi šikovné konstrukce (řekl bych až revoluční), která umožňuje z metody vracet více hodnot, aniž by vývojář musel Tuple ručně vytvářet (ten je vytvořen na pozadí za něj) nebo používat modifikátor parametru out (který má navíc nevýhodu, že jej nelze použít pro asynchronní metody).

Nová syntaxe dovoluje vracet n-tici z jakékoli metody. Zápis vypadá takto:

(int min, int max) GetExtremes(int[] values) { int min, max; ... return (min, max); }

Hodnotu pak získáváme spíše než přes klasické var opět speciálním způsobem, tzv. dekonstrukcí (která byla už úspěšně zavedena např. v EcmaScript6):

(int min, int max) = GetExtremes({1,2,3});

Dekonstrukcí přiřazujeme do více proměnných zároveň. Tyto proměnné můžeme nově deklarovat, nebo použít už existující.

Jejich chování ještě rozšiřuje následující podverze C# 7.

Zahození (discard)

Pokud nás některý člen n-tice nezajímá a nechceme pro něj zbytečně vytvářet proměnnou, můžeme použít další syntaktický obrat, a tím je uvedení podtržítka:

(int min, _) = GetExtremes({1,2,3});

Zahození lze použít i pro out parametry metod, pokud nás jejich výsledná hodnota nezajímá.


Optimalizace výkonu

Ref returns, ref locals

C# 7 zavádí optimalizaci ne nepodobnou ukazatelům z unsafe prostředí, a to pro hodnotové typy (referenční typy logicky svou referenci mají od začátku). "ref int i" se dá do jisté míry chápat jako ukazatel na hodnotu typu int. Používá se hlavně pro návrat hodnoty z metody, resp. nevrací se ani tak hodnota hodnotového typu jako reference na ni. Nemusí se tak vytvářet nová proměnná a do ní kopírovat hodnota ze vstupu, ale vrátí se přímo reference na ni – např. pokud bylo účelem metody vyhledat ji ve složitější struktuře.

Ref locals

Navrácenou hodnotu lze převzít klasicky kopií do jiné proměnné – pak použijeme klasický zápis

var x = Get();

a vlastně vůbec nepozorujeme, že je nám dána možnost převzít hodnotu referencí. A nebo se rozhodneme právě pro převzetí reference, pak už ale musíme uvést, že to chceme - na obou stranách výrazu musíme uvést klíčové slůvko ref.

ref int i = ref Get();

Celý výraz pak čteme: do reference na proměnnou typu int pojmenovanou "i" přiřadíme navrácenou referenci z metody Get().

ValueTask

Pokud asynchronní metoda končí synchronně, tj. řízení toku programu se k volání await nedostane, pak se návratový objekt Tasku vytváří zbytečně a tím neúčelně odčerpává systémové prostředky - tvorba objektu Tasku je relativně pomalá, pozdějí navíc může program opět zpomalit odbourávání objektu garbage collectorem. Pro případ, kdy se metoda chová častěji synchronně než asynchronně (např. jednou si zavolá službu a pak nějakou dobu jen kešuje výsledek) je tu možnost místo návratového typu Tasku použít typ podobný, ale hodnotový - ValueTask. Ten vytváří nad Taskem rozhodovací mezivrstvu, která odstiňuje potřebu tvorby Tasku právě v synchronních případech. Je ale třeba přemýšlet o výše uvedených podmínkách – pokud metoda naopak await volá často, pak se Task stejně instanciuje, tentokrát ale ještě s režií navíc.