Sdílet prostřednictvím


Přehled nových funkcí v C# 6

Verze 6 jazyka C# se nadále vyvíjí, aby měl méně často používaný kód, zlepšila srozumitelnost a větší konzistenci. Čistější inicializační syntaxe, možnost použít operátor await v blocích catch/finally a podmíněnou hodnotu null? jsou užitečné zejména.

Poznámka:

Informace o nejnovější verzi jazyka C# – verze 7 – najdete v článku Co je nového v jazyce C# 7.0.

Tento dokument představuje nové funkce jazyka C# 6. Mono kompilátor je plně podporovaný a vývojáři můžou začít používat nové funkce na všech cílových platformách Xamarinu.

Co je nového ve videu c# 6

Použití jazyka C# 6

Kompilátor C# 6 se používá ve všech nedávných verzích Visual Studio pro Mac. Ti, kteří používají kompilátory příkazového řádku, by měli potvrdit, že mcs --version vrací hodnotu 4,0 nebo vyšší. Visual Studio pro Mac uživatelé můžou zkontrolovat, jestli mají nainstalovanou verzi Mono 4 (nebo novější).Informace o zobrazení podrobností Visual Studio pro Mac > Visual Studio pro Mac>.

Méně často používané

pomocí statického

Výčty a některé třídy, jako System.Mathjsou , jsou primárně držiteli statických hodnot a funkcí. V jazyce C# 6 můžete importovat všechny statické členy typu pomocí jediného using static příkazu. Porovnejte typickou trigonometrickou funkci v jazyce C# 5 a C# 6:

// Classic C#
class MyClass
{
    public static Tuple<double,double> SolarAngleOld(double latitude, double declination, double hourAngle)
    {
        var tmp = Math.Sin (latitude) * Math.Sin (declination) + Math.Cos (latitude) * Math.Cos (declination) * Math.Cos (hourAngle);
        return Tuple.Create (Math.Asin (tmp), Math.Acos (tmp));
    }
}

// C# 6
using static System.Math;

class MyClass
{
    public static Tuple<double, double> SolarAngleNew(double latitude, double declination, double hourAngle)
    {
        var tmp = Asin (latitude) * Sin (declination) + Cos (latitude) * Cos (declination) * Cos (hourAngle);
        return Tuple.Create (Asin (tmp), Acos (tmp));
    }
}

using static neuvádí veřejná const pole, například Math.PI a Math.E, přímo přístupná:

for (var angle = 0.0; angle <= Math.PI * 2.0; angle += Math.PI / 8) ... 
//PI is const, not static, so requires Math.PI

používání statických metod s metodami rozšíření

Zařízení using static funguje trochu jinak s rozšiřujícími metodami. I když se metody rozšíření zapisují pomocí static, nemají smysl bez instance, na které se má pracovat. using static Pokud se tedy použije s typem, který definuje rozšiřující metody, budou metody rozšíření k dispozici pro jejich cílový typ (typ metodythis). Můžete například using static System.Linq.Enumerable použít k rozšíření rozhraní API IEnumerable<T> objektů bez přenesení všech typů LINQ:

using static System.Linq.Enumerable;
using static System.String;

class Program
{
    static void Main()
    {
        var values = new int[] { 1, 2, 3, 4 };
        var evenValues = values.Where (i => i % 2 == 0);
        System.Console.WriteLine (Join(",", evenValues));
    }
}

Předchozí příklad ukazuje rozdíl v chování: rozšiřující metoda Enumerable.Where je přidružena k poli, zatímco statická metoda String.Join lze volat bez odkazu na String typ.

nameof – výrazy

Někdy chcete odkazovat na název, který jste zadali proměnnou nebo pole. V jazyce C# 6 nameof(someVariableOrFieldOrType) vrátí řetězec "someVariableOrFieldOrType". Například při vyvolání velmi pravděpodobného ArgumentException názvu, který argument je neplatný:

throw new ArgumentException ("Problem with " + nameof(myInvalidArgument))

Hlavní výhodou výrazů nameof je, že jsou kontrolovány na typech a jsou kompatibilní s refaktoringem založeným na nástrojích. Kontrola typů nameof výrazů je obzvláště vítána v situacích, kdy string se používá k dynamickému přidružování typů. Například v iOSu string se používá k určení typu použitého k prototypu UITableViewCell objektů v objektu UITableView. nameof může zajistit, že toto přidružení selžou kvůli chybně napsané nebo sloppy refaktoring:

public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
    var cell = tableView.DequeueReusableCell (nameof(CellTypeA), indexPath);
    cell.TextLabel.Text = objects [indexPath.Row].ToString ();
    return cell;
}

I když můžete předat kvalifikovaný název nameof, vrátí se pouze konečný prvek (po posledním .). Můžete například přidat datovou vazbu v Xamarin.Forms:

var myReactiveInstance = new ReactiveType ();
var myLabelOld.BindingContext = myReactiveInstance;
var myLabelNew.BindingContext = myReactiveInstance;
var myLabelOld.SetBinding (Label.TextProperty, "StringField");
var myLabelNew.SetBinding (Label.TextProperty, nameof(ReactiveType.StringField));

Dvě volání, která mají předávat SetBinding identické hodnoty: nameof(ReactiveType.StringField) není "StringField"to tak, "ReactiveType.StringField" jak byste mohli původně očekávat.

Podmíněný operátor s hodnotou Null

Dřívější aktualizace jazyka C# zavedly koncepty typů s možnou hodnotou null a operátor ?? null-coalescing, aby se snížil objem často používaného kódu při zpracování hodnot null. C# 6 pokračuje v tomto motivu pomocí operátoru ?.s podmínkou null . Pokud se používá na objektu na pravé straně výrazu, vrátí operátor s podmínkou null hodnotu členu, pokud objekt není null a null jinak:

var ss = new string[] { "Foo", null };
var length0 = ss [0]?.Length; // 3
var length1 = ss [1]?.Length; // null
var lengths = ss.Select (s => s?.Length ?? 0); //[3, 0]

(Obě length0 a length1 jsou odvozeny jako typ int?)

Poslední řádek v předchozím příkladu ukazuje ? operátor s podmínkou null v kombinaci s operátorem ?? null-coalescing. Nový operátor C# 6 s podmínkou null vrátí null 2. prvek v matici, v němž operátor nulového sjednocení nakopne a dodává 0 do lengths pole (zda je to vhodné nebo není, samozřejmě problém-specifická).

Podmíněný operátor s hodnotou null by měl výrazně snížit množství často používané kontroly null nezbytné v mnoha aplikacích.

Kvůli nejednoznačnostem existují určitá omezení podmíněného operátoru s hodnotou null. Nelze okamžitě sledovat ? seznam argumentů se závorkou, protože můžete doufat, že to uděláte s delegátem:

SomeDelegate?("Some Argument") // Not allowed

Invoke Lze však použít k oddělení ? od seznamu argumentů a je stále označeným vylepšením nad kontrolním nullblokem často používaného typu:

public event EventHandler HandoffOccurred;
public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
    HandoffOccurred?.Invoke (this, userActivity.UserInfo);
    return true;
}

Interpolace řetězců

Funkce String.Format tradičně používala indexy jako zástupné symboly v řetězci formátu, například String.Format("Expected: {0} Received: {1}.", expected, received). Přidání nové hodnoty samozřejmě vždy zahrnovalo otravný malý úkol počítání argumentů, přečíslování zástupných symbolů a vložení nového argumentu do správného pořadí v seznamu argumentů.

Nová funkce interpolace řetězců jazyka C# 6 výrazně zlepšuje String.Format. Nyní můžete přímo pojmenovat proměnné v řetězci s předponou .$ Například:

$"Expected: {expected} Received: {received}."

Proměnné jsou samozřejmě zaškrtnuté a chybně napsaná nebo nedostupná proměnná způsobí chybu kompilátoru.

Zástupné symboly nemusí být jednoduché proměnné, můžou se jednat o libovolný výraz. V těchto zástupných symbolech můžete použít uvozovky bez uvozovek. Všimněte si "s" například následující:

var s = $"Timestamp: {DateTime.Now.ToString ("s", System.Globalization.CultureInfo.InvariantCulture )}"

Interpolace řetězců podporuje syntaxi zarovnání a formátování .String.Format Stejně jako jste předtím napsali {index, alignment:format}, v C# 6 píšete {placeholder, alignment:format}:

using static System.Linq.Enumerable;
using System;

class Program
{
    static void Main ()
    {
        var values = new int[] { 1, 2, 3, 4, 12, 123456 };
        foreach (var s in values.Select (i => $"The value is { i,10:N2}.")) {
            Console.WriteLine (s);
        }
    Console.WriteLine ($"Minimum is { values.Min(i => i):N2}.");
    }
}

výsledky:

The value is       1.00.
The value is       2.00.
The value is       3.00.
The value is       4.00.
The value is      12.00.
The value is 123,456.00.
Minimum is 1.00.

Interpolace řetězců je syntaktický cukr pro String.Format: nelze jej použít s @"" řetězcovými literály a není kompatibilní s const, i když nejsou použity žádné zástupné symboly:

const string s = $"Foo"; //Error : const requires value

V běžném případě použití argumentů funkce s interpolací řetězců je stále potřeba dávat pozor na únik, kódování a problémy s jazykovou verzí. Dotazy SQL a ADRESY URL jsou samozřejmě důležité pro sanitizaci. String.FormatStejně jako v případě , interpolace řetězců používá CultureInfo.CurrentCulture. Použitíjech CultureInfo.InvariantCulture

Thread.CurrentThread.CurrentCulture  = new CultureInfo ("de");
Console.WriteLine ($"Today is: {DateTime.Now}"); //"21.05.2015 13:52:51"
Console.WriteLine ($"Today is: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}"); //"05/21/2015 13:52:51"

Inicializace

C# 6 poskytuje řadu stručných způsobů, jak zadat vlastnosti, pole a členy.

Automatická inicializace vlastností

Automatické vlastnosti se teď dají inicializovat stejným stručným způsobem jako pole. Neměnné automatické vlastnosti lze zapsat pouze pomocí getteru:

class ToDo
{
    public DateTime Due { get; set; } = DateTime.Now.AddDays(1);
    public DateTime Created { get; } = DateTime.Now;

V konstruktoru můžete nastavit hodnotu automatické vlastnosti getter-only:

class ToDo
{
    public DateTime Due { get; set; } = DateTime.Now.AddDays(1);
    public DateTime Created { get; } = DateTime.Now;
    public string Description { get; }

    public ToDo (string description)
    {
        this.Description = description; //Can assign (only in constructor!)
    }

Tato inicializace automatických vlastností je obecná funkce pro úsporu místa i boon pro vývojáře, kteří chtějí zdůraznit neměnnost objektů.

Inicializátory indexu

Jazyk C# 6 zavádí inicializátory indexů, které umožňují nastavit klíč i hodnotu v typech, které mají indexer. Obvykle se jedná o Dictionarydatové struktury ve stylu:

partial void ActivateHandoffClicked (WatchKit.WKInterfaceButton sender)
{
    var userInfo = new NSMutableDictionary {
        ["Created"] = NSDate.Now,
        ["Due"] = NSDate.Now.AddSeconds(60 * 60 * 24),
        ["Task"] = Description
    };
    UpdateUserActivity ("com.xamarin.ToDo.edit", userInfo, null);
    statusLabel.SetText ("Check phone");
}

Členové funkce založené na výrazu

Funkce lambda mají několik výhod, z nichž jedna jednoduše šetří místo. Podobně členové třídy s použitím výrazů umožňují vyjádřit malé funkce trochu stručněji, než bylo možné v předchozích verzích jazyka C# 6.

Členové funkce s použitím výrazu používají namísto tradiční syntaxe bloku syntaxi šipky lambda:

public override string ToString () => $"{FirstName} {LastName}";

Všimněte si, že syntaxe lambda-arrow nepoužívá explicitní return. U funkcí, které vracejí voidvýraz, musí být také příkazem:

public void Log(string message) => System.Console.WriteLine($"{DateTime.Now.ToString ("s", System.Globalization.CultureInfo.InvariantCulture )}: {message}");

Na členy s body výrazu se stále vztahuje pravidlo, které je podporováno pro metody, async ale ne vlastnosti:

//A method, so async is valid
public async Task DelayInSeconds(int seconds) => await Task.Delay(seconds * 1000);
//The following property will not compile
public async Task<int> LeisureHours => await Task.FromResult<char> (DateTime.Now.DayOfWeek.ToString().First()) == 'S' ? 16 : 5;

Výjimky

Neexistuje žádných dvou způsobů: zpracování výjimek je obtížné správně. Nové funkce v C# 6 umožňují flexibilnější a konzistentnější zpracování výjimek.

Filtry výjimek

Podle definice dochází k výjimkám za neobvyklých okolností a může být velmi obtížné zdůvodnění a kód o všech způsobech, jak může dojít k výjimce určitého typu. C# 6 zavádí možnost chránit obslužnou rutinu provádění s filtrem vyhodnoceným modulem runtime. To se provádí přidáním when (bool) vzoru za normální catch(ExceptionType) deklaraci. V následujícím příkladu filtr rozlišuje chybu analýzy související s date parametrem, a nikoli jiné chyby analýzy.

public void ExceptionFilters(string aFloat, string date, string anInt)
{
    try
    {
        var f = Double.Parse(aFloat);
        var d = DateTime.Parse(date);
        var n = Int32.Parse(anInt);
    } catch (FormatException e) when (e.Message.IndexOf("DateTime") > -1) {
        Console.WriteLine ($"Problem parsing \"{nameof(date)}\" argument");
    } catch (FormatException x) {
        Console.WriteLine ("Problem parsing some other argument");
    }
}

await in catch... Konečně...

Funkce async představené v jazyce C# 5 byly pro tento jazyk změnou her. V jazyce C# 5 await nebylo povoleno v catch blocích a finally v blocích byla nesměšněna vzhledem k hodnotě async/await schopnosti. C# 6 toto omezení odebere, což umožňuje konzistentně čekat asynchronní výsledky prostřednictvím programu, jak je znázorněno v následujícím fragmentu kódu:

async void SomeMethod()
{
    try {
        //...etc...
    } catch (Exception x) {
        var diagnosticData = await GenerateDiagnosticsAsync (x);
        Logger.log (diagnosticData);
    } finally {
        await someObject.FinalizeAsync ();
    }
}

Shrnutí

Jazyk C# se dále vyvíjí, aby byl vývojáři produktivnější a zároveň podporovali osvědčené postupy a podpůrné nástroje. Tento dokument poskytl přehled nových jazykových funkcí v jazyce C# 6 a stručně ukázal, jak se používají.