Přehled nových funkcí v C# 6
Verze 6 jazyka C# nadále vyvíjí jazyk, aby měl méně často používaný kód, lepší přehlednost a větší konzistenci. Syntaxe inicializace čištění, schopnost použít await v blocích catch/finally a s hodnotou null-Conditioned? operátor je zvláště užitečný.
Poznámka
Informace o nejnovější verzi jazyka C# – verze 7 – Přečtěte si článek novinky v c# 7,0
Tento dokument představuje nové funkce C# 6. Je plně podporovaný kompilátorem mono a vývojáři můžou začít používat nové funkce napříč všemi cílovými platformami Xamarin.
Co je nového v C# 6 video
Pomocí C# 6
kompilátor C# 6 se používá ve všech nejnovějších verzích Visual Studio pro Mac.
Ty, které používají kompilátory příkazového řádku, by měly potvrdit, že mcs --version vrátí 4,0 nebo vyšší.
Visual Studio pro Mac uživatelé můžou zjistit, jestli mají nainstalovanou Mono 4 (nebo novější), a to odkazem na informace o Visual Studio pro Mac Visual Studio pro Mac > zobrazit podrobnosti.
Méně často používaný
Použití static
Výčty a určité třídy System.Math , jako jsou, jsou primárně držiteli statických hodnot a funkcí. V C# 6 můžete importovat všechny statické členy typu jediným using static příkazem. Porovnat typickou trigonometrické 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 nezveřejňuje veřejná const pole, jako jsou Math.PI a Math.E , přímo dostupná:
for (var angle = 0.0; angle <= Math.PI * 2.0; angle += Math.PI / 8) ...
//PI is const, not static, so requires Math.PI
použití statických metod rozšíření
using staticZařízení funguje trochu jinak s rozšiřujícími metodami. I když jsou metody rozšíření zapisovány pomocí static , nedávají smysl bez instance, na které se má pracovat. Takže pokud using static se používá s typem, který definuje metody rozšíření, metody rozšíření budou k dispozici na svém cílovém typu (typ metody this ). Například using static System.Linq.Enumerable lze použít k rozšiřování rozhraní API IEnumerable<T> objektů bez nutnosti přecházet do 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í: metoda rozšíření Enumerable.Where je přidružena k poli, zatímco statická metoda String.Join může být volána bez odkazu na String typ.
Výrazy nameof
V některých případech chcete odkazovat na název, kterému jste předali proměnnou nebo pole. V C# 6 nameof(someVariableOrFieldOrType) vrátí řetězec "someVariableOrFieldOrType" . Například při vyvolání je ArgumentException velmi pravděpodobně vhodné pojmenovat, který argument je neplatný:
throw new ArgumentException ("Problem with " + nameof(myInvalidArgument))
Hlavní výhodou nameof výrazů je, že jsou typy zkontrolovány a jsou kompatibilní s refaktoringem se zapnutým nástrojem. Kontrola typu nameof výrazů je obzvláště úvodní v situacích, kdy string se používá k dynamickému přidružení typů. Například v iOS a string se používá k určení typu používaného k prototypování UITableViewCell objektů v UITableView . nameof může zajistit, že přidružení neselže z důvodu chybného nebo refaktoringu neuspořádaném:
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 poslední prvek (za poslední . ). Datovou vazbu můžete například přidat 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á SetBinding přecházejí identické hodnoty: nameof(ReactiveType.StringField) je "StringField" , ne "ReactiveType.StringField" tak, jak byste mohli původně očekávat.
Podmíněný operátor s hodnotou null
Dřívější aktualizace jazyka C# představily koncepty typů s možnou hodnotou null a operátoru pro sjednocení s hodnotou null ?? pro snížení množství často používaného kódu při zpracování hodnot s možnou hodnotou null. Jazyk C# 6 pokračuje v tomto motivu s operátorem "null-Conditional" ?. . Při použití na objektu na pravé straně výrazu vrátí operátor null-Condition hodnotu člena, pokud objekt není null a null v opačném případě:
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]
( length0 A length1 jsou odvozeny jako typ int? )
Poslední řádek v předchozím příkladu ukazuje ? operátor s hodnotou null v kombinaci s ?? operátorem slučování null. Nový operátor s hodnotou null-Conditional v C# 6 se vrátí null na druhý prvek v poli, v němž je umístěn operátor sloučení s hodnotou null a zadá 0 do lengths pole (ať už je to vhodné, nebo není samozřejmě konkrétní problém).
Operátor s hodnotou null by měl výrazně snížit množství často standardizované kontroly hodnoty null, které jsou nezbytné pro mnoho aplikací.
Existují určitá omezení pro podmíněný operátor s hodnotou null z důvodu nejednoznačnosti. Seznam argumentů, které se v závorkách nedají použít, můžete hned sledovat pomocí ? delegáta:
SomeDelegate?("Some Argument") // Not allowed
Dá se ale Invoke použít k oddělení ? od seznamu argumentů a pořád se jedná o označené vylepšení přes null kontrolní blok často používaného textu:
public event EventHandler HandoffOccurred;
public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
HandoffOccurred?.Invoke (this, userActivity.UserInfo);
return true;
}
Interpolace řetězců
String.FormatFunkce tradičně používala indexy jako zástupné symboly ve formátu řetězce, například, String.Format("Expected: {0} Received: {1}.", expected, received ). Přidáním nové hodnoty se samozřejmě vždycky zabývají nejenom nepříjemném malým úkolem při počítání argumentů, přečíslování zástupných symbolů a vložením nového argumentu do pravé posloupnosti v seznamu argumentů.
Nová interpolace řetězcové funkce v C# 6 výrazně vylepš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ě, je-li zaškrtnuto a nesprávně napsaná nebo nedostupná proměnná způsobí chybu kompilátoru.
Zástupné symboly nemusejí být jednoduché proměnné, můžou být libovolný výraz. V rámci těchto zástupných symbolů můžete použít uvozovky bez uvozovacích těchto nabídek. Všimněte si například "s" následujícího:
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 napíš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 : nemůže být použit 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 případě běžného použití v případě sestavování argumentů funkce s interpolací řetězce je stále nutné mít pozor na problémy s uvozovacími znaky, kódováním a kulturou. SQL a dotazy URL jsou samozřejmě kritické pro úpravu. Stejně jako u String.Format , interpolace řetězce používá CultureInfo.CurrentCulture . Použití CultureInfo.InvariantCulture je trochu více slov:
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
Jazyk C# 6 poskytuje několik stručných způsobů, jak zadat vlastnosti, pole a členy.
Inicializace automatických vlastností
Automatické vlastnosti se teď dají inicializovat stejným stručným způsobem jako pole. Neměnné automatické vlastnosti se dají zapisovat jenom pomocí metody getter:
class ToDo
{
public DateTime Due { get; set; } = DateTime.Now.AddDays(1);
public DateTime Created { get; } = DateTime.Now;
V konstruktoru můžete nastavit hodnotu vlastnosti auto-Property pouze pro getter:
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 ukládání prostoru a Boon vývojářům, kteří chtějí zdůraznit neměnnosti ve svých objektech.
Inicializátory indexů
C# 6 zavádí Inicializátory indexů, které umožňují nastavit klíč i hodnotu v typech, které mají indexer. Obvykle je to pro Dictionary datové 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 Expression-těle
Funkce lambda mají několik výhod, z nichž jeden je pouhým uložením místa. Podobně, členové třídy Expression-těle umožňují, aby byly malé funkce v předchozích verzích C# 6 vyjádřeny trochu stručnější, než bylo možné.
Členové funkce Expression-těle používají syntaxi šipky lambda místo tradiční syntaxe bloku:
public override string ToString () => $"{FirstName} {LastName}";
Všimněte si, že syntaxe lambda-šipky nepoužívá explicitní return . U funkcí, které vrací void , musí být výraz také příkaz:
public void Log(string message) => System.Console.WriteLine($"{DateTime.Now.ToString ("s", System.Globalization.CultureInfo.InvariantCulture )}: {message}");
U členů Expression-těle se stále vztahují pravidlo, které async je podporováno pro metody, ale ne pro 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
Neexistují dva způsoby: zpracování výjimek je obtížné získat. Nové funkce v C# 6 umožňují zpracování výjimek flexibilnější a konzistentní.
Filtry výjimek
Podle definice dojde k výjimkám za neobvyklých okolností a může být velmi obtížné důvod a kód o všech způsobech, ke kterým může dojít výjimka určitého typu. Jazyk C# 6 zavádí možnost ochrany obslužné rutiny spouštění pomocí filtru s vyhodnoceným modulem runtime. To se provádí přidáním when (bool) vzoru za normální catch(ExceptionType) deklarací. V následujícím filtru rozlišuje filtr chybu analýzy související s date parametrem na rozdíl od jiných chyb 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");
}
}
očekává se v catch... nakonec...
asyncMožnosti zavedené v C# 5 byly Game měnič pro daný jazyk. V jazyce C# 5 await nebyl povolen v catch blocích a finally , obtěžování předané hodnotou async/await funkce. Jazyk C# 6 toto omezení odstraní, což umožňuje, aby byly asynchronní výsledky v programu konzistentní, 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 ();
}
}
Souhrn
Jazyk C# bude i nadále vyvíjet, aby vývojáři měli větší produktivitu a zároveň podporovali dobré postupy a podpůrné nástroje. Tento dokument má přehled o nových jazykových funkcích v C# 6 a krátce ukázal, jak se používají.