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.Math
jsou , 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 null
blokem č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.Format
Stejně 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 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 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í void
vý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í.