Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

Nowości w C# 6 — cóż ciekawego otrzymujemy?

Tak, tak, tak. C# 6 jest już z nami od jakiegoś już czasu, ale w życiu nie jest tak kolorowo i nie wszyscy mogli przejść na nowego Visual Studio 2015 tuż po tym jak się ukazał. Dodatkowo nawet jeśli ktoś już przesiadł się na najświeższe IDE od MS, to i tak nie zawsze mógł używać nowości, które wpadły wraz z C# 6.

Zatem dla niektórych będzie to przypomnienie, dla innych zapoznanie się z nowościami. Co więcej, w sieci jest wiele stron opisujących nowe elementy w C#, które... nie znalazły się w finalnym wydaniu.

Sam C# 6 nie przynosi olbrzymich zmian czy nowości. W tym wydaniu nastawiono się głównie na wprowadzenie małych ficzerów, które uprzyjemnią pracę z kodem i zmniejszą jego ilość, zwiększając przy tym czytelność.

Cóż ciekawego pojawi się zatem w wraz z C# 6?

Operator ?.

To chyba jedna z bardziej wyczekiwanych nowości w C# 6. Zmorą deweloperów tworzących w C# jest wyjątek NullReferenceException. Powoduje to często, że kod w wielu miejscach złożony jest if-ów, w których sprawdzamy czy coś nie jest nullem.

Klasycznie: if (client != null && client.Baskets != null && client.Baskets.Any(x => x.Items != null && x.Items.Length > 1)) { Console.WriteLine("OK!"); }

Od teraz możemy zastąpić sprawdzanie czy zmienna nie jest nullem poprzez użycie operatora ?. zwanego potocznie Elvis operatorem (przekręćcie głowę, aby zobaczyć Króla RnR)

C# 6: if (client?.Baskets?.Any(x => x.Items?.Length > 0) ?? false) { Console.WriteLine("OK!"); }

Warto odnotować, że używając operatora możemy uprzyjemnić sobie życie z delegatami.

Do tej pory jeśli chcieliśmy odpalić delagat, dbając o wątki, należało zrobić to w następujący sposób:

var onChanged = OnChanged; if (onChanged != null) { onChanged(this, args); }

Za pomocą ?. ten sam kod (thread-safe) w C# 6 napiszemy:

OnChanged?.Invoke(this, args);

Właściwości - inicjalizacja

Możemy już inicjalizować właściwości, podobnie jak w przypadku pól:

public class Customer { public string First { get; set; } = "Jon"; public string Last { get; set; } = "Snow"; }

Warto dodać, że inicjalizacja nie przebiega poprzez set, ale wartość jest nadawana bezpośrednio.

Właściwości - readonly

public class Customer { public string First { get; } = "Jon"; public string Last { get; } public Customer(string last) { Last = last; } }

Właściwości można od teraz tworzyć bez użycia settera. W takim wypadku niejawnie tworzone jest pole readonly. Właściwość może być znacjonalizowana tylko bezpośrednio (jak wyżej) lub w konstruktorze.

String interpolation

Kolejną ciekawią nowością jest interpolacja Stringów. Zamiast używać String.Format możemy to samo zrobić w znacznie krótszy sposób:

string first = "Jon", last = "Snow"; var s_old = String.Format("{0} likes {1}", first, last); var s_csharp6 = $"{first} likes {last} now {DateTime.Now:d}";

Metody, właściwości i indeksatory jako pojedyncze wyrażenie lambda

W C# 6 dostaliśmy możliwość zapisywania metod, będących pojedynczymi wyrażeniami, w prostej i zwięzłej formule. Oczywiście metody takie mogą również zwracać void.

Klasycznie: public decimal AddMoney(decimal toAdd) { return Money += toAdd; }

C# 6: public decimal AddMoney(decimal toAdd) => Money += toAdd;

Funkcjonalność ta została rozszerzona także o właściwości i indeksatory. W przypadku tych pierwszych tworzymy wyliczanlne właściwości (tylko do odczytu).

Klasycznie: public string FullName { get { return First + " " + Last; } }

C# 6: public string FullName => First + " " + Last;

W ten sposób otrzymujemy znacznie lżejszy kod od strony wizualnej. Czytelność jest już jednak miejscami dyskusyjna.

public class Customer { public string First { get; } = "Jon"; public string Last { get; } = "Snow"; public decimal Money { get; set; } = 100; private string[] values = new string[100]; //metody public decimal AddMoney(decimal toAdd) => Money += toAdd; public void Print() => Console.WriteLine(FullName); //właściwości public string FullName => First + " " + Last; //indeksator public string this[long id] => id >= 0 && id < values.Length ? values[id] : null; }

Using static

W najnowszej wersji C# możemy korzystać z fleczeru, który pozwala na używanie dostępnych statycznych elementów w klasach czy Enumach.

Klasycznie: class Program { static void Main() { Console.WriteLine(Math.Sqrt(10)); Console.WriteLine(DayOfWeek.Friday - DayOfWeek.Monday); } } C# 6: using static System.Console; using static System.Math; using static System.DayOfWeek; class Program { static void Main() { WriteLine(Sqrt(10)); WriteLine(Friday - Monday); } }

W ten sposób można jednak łatwo skomplikować sobie życie.

Pytanie: czy Sqrt(10) przejdzie do metody z klasy System.Math czy Program, a może wyskoczy wyjątek przy kompilacji?

using static System.Console; using static System.Math; using static System.DayOfWeek; class Program { static void Main() { WriteLine(Sqrt(10)); WriteLine(Friday - Monday); } int Sqrt(int x) { return x - 3; } }

Ficzer całkiem ciekawy. Z jednej strony zmniejsza ilość kodu i poprawia jego czytelność, ale z drugiej może wprowadzać pewnie niejasności w zrozumieniu kontekstu. Using static zapewne najlepiej nada się do użycia dla kilku ściśle wybranych klas, aby nie utrudnić analizy kodu.

nameof

C# 6 wprowadza również wyrażenie nameof, które zwraca stringa będącego nazwą zmiennej, właściwości, klasy czy metody:

return nameof(var1) //"var1" return nameof(collection.Item.Index) //"Index" Idealnie nada się do wywołania PropertyChanged, wyrzucania wyjątków z informacją o nazwie zmiennej, czy operując na typach generycznych

if (value== null) throw new ArgumentNullException(nameof(value)+ " is null :(");

Filtrowanie wyjątków

W nowej wersji języka dostaliśmy możliwość dodawania filtrów do wyjątków:

try { throw new Exception("My exception"); } catch (Exception ex) when (ex.Message == "My exception") { Console.WriteLine("My exception caught"); } catch (Exception ex) { Console.WriteLine("Other exception caught here"); }

Pozostałe zmiany:

  • nowe, bardziej przyjazne inicjalizery do słowników:

    Klasycznie: var dic = new Dictionary<string, int> { {"x", 1}, {"y", 2} }; C# 6: var dic = new Dictionary<string, int> { ["x"] = "1", ["y"] = "2", };

  • await w catch i finally

    C# 6 pozwala już na używanie await w blokach catch i finally. Podobno Microsoft użył sporej dawki magii, aby to zaimplementować (na początku twierdzili, że się nie da), ale finalnie udało się:

    try { … } catch(Exception e) { await LogAsync(e); } finally { await Close(); }

  • stworzone Extension method Add w kolekcji zostanie użyte przy inicjalizacji kolekcji:

    namespace MyExtensions { public static class DicExt { public static void Add(this Dictionary<string, int> dic, int value) { dic.Add("Number " + no.ToString(), value); } } } using MyExtensions var dic = new Dictionary<string, int> { 1, 4, 10 };

  • do numerów z błędami w #pragma warning disable/restore doszedł opcjonalny prefix "CS"
  • usprawniono mechanizm przeciążeń

To tyle :) Nie wszystkie zmiany są szczególnie ciekawe, a kilka z nich może nawet delikatnie utrudnić późniejszą analizę kodu, jeśli będą używane w nadmiarze i w niewłaściwy sposób. Metody, właściwości i indeksatory jako pojedyncze wyrażenie lambda - tutaj jeśli za dużo zechcemy upchać do wyrażeń, wówczas otrzymamy jednolinijkowe potwory. W przypadku using static trzeba uważać na to, aby niechcący nie pomieszać kontekstów, co innego że ficzer jest racze zbędnym bajerem w codziennym kodowaniu.

Ogólnie warto jednak odnotować, że pojawił się nareszcie Elvis, który zapewne będzie często używany przez programistów. Również zmiany we właściwościach są ciekawą opcją, podobnie jak nameof i interpolacja stringów. Pozostałe rzeczy są raczej małymi ułatwieniami, których możemy nawet nie zauważyć.  

windows programowanie

Komentarze

0 nowych
tylko_prawda   11 #1 18.07.2016 20:50

Jak dla mnie C++ jest łatwiejszy. A po przeczytaniu tego wpisu tylko się upewniłem, że tak jest.

dragon321   10 #2 18.07.2016 21:07

@tylko_prawda: Mi tam jako programisty C++ i ewentualnie Javy nie podoba się w C# to, że nazwy metod zaczynają się z wielkiej litery. Przez to kod w tym języku jakoś wydaje mi się nie "naturalny".

Inne języki mają metody nazywane z małych liter, a Microsoft nie byłby sobą, gdyby nie zrobił inaczej niż reszta świata.

KoczurekK   10 #3 18.07.2016 21:17

No autorze rzeczywiście się nie pospieszyłeś bo jako zwolennik C++ i D a spory przeciwnik C# znałem część tych ficzerów. ;-;
Inna sprawa że nie wszystkie mi się podobają, ryzyko wystąpienia nulli w takim kodzie świadczy o błędach w projektowaniu całego projektu. ;) Btw jeśli chodzi o obj != null, to zapis !obj nie jest dozwolony i równoznaczny? W C++ można robić taką magię ze wskaźnikami bo nullptr (NULL chyba już nikt nie używa, prawda?) rzutowane na bool daje false.

djfoxer   18 #4 18.07.2016 21:32

@tylko_prawda: Ale mówisz o takim czystym, czystym C++? Bo jeśli tak, to jak przypomnę sobie ze studiów zabawy z allokacją pamięci i późniejszym zwalnianiem jej to tak raczej średnio :P

Poza tym to jest tylko wycinek jakiś tam specyficznych nowych ficzerów, które nie obrazują języka C# w pełni.

djfoxer   18 #5 18.07.2016 21:44

@dragon321: Kwestia przyzwyczajenia, zaś PascalCase w C# jest stosowany, gdyż przy tworzeniu języka C# uczestniczył Anders Hejlsberg, twórca Turbo Pascala i architekt Delphi. Więc nie jest to przypadek i nie zostało to ot tak wyssane z palca ;) Taka ciekawostka :)

tylko_prawda   11 #6 18.07.2016 22:03

@djfoxer: No z alokacją i zwalnianiem może średnio, ale ogólnie C++ wydaje mi się jakieś logczniejsze. Ale każdy pisze w czym chce :)

djfoxer   18 #7 18.07.2016 22:34

@KoczurekK: "Inna sprawa że nie wszystkie mi się podobają, ryzyko wystąpienia nulli w takim kodzie świadczy o błędach w projektowaniu całego projektu. " - nie przesadzajmy, null jest literałem, takim specjalnym, ale określa pewną wartość (a dokładniej jej brak). Oczywiście lepiej jest zwracać niepuste kolekcje, ale czasem null może świadczyć np. o braku/nieistnieniu czegoś.

"Btw jeśli chodzi o obj != null, to zapis !obj nie jest dozwolony i równoznaczny?" Nie, w C# jeśli chcesz sprawdzić czy obiekt nie jest nullem musisz zrobić to jawnie czyli, obiekt != null. Null nie można bezpośrednio porównać do 0 czy false, gdyż zmienna typu int/bool nie może być null (może być oczywiście typ nullowalny, int?, bool? ...)

djfoxer   18 #8 18.07.2016 22:47

@tylko_prawda: C# stawia na co innego niż C++ (szybkość pisania kodu vs wydajność stworzonego kodu). Ciężko tu porównywać te dwa języki tak bezpośrednio.

TestamenT   12 #9 19.07.2016 00:08

@dragon321: "nie podoba się w C# to, że nazwy metod zaczynają się z wielkiej litery"
Mnie się to podoba w Go. Bo tworząc bibliotekę wszystkie funkcje, struktury, zmienne globalne i te w strukturach pisane z wielkiej litery są widziane na zewnątrz biblioteki.
A to co jest z małej litery jest widziane tylko wewnątrz biblioteki.

@tylko_prawda "Ale każdy pisze w czym chce "
Gdyby można było tak zawsze.

KoczurekK   10 #10 19.07.2016 01:11

@djfoxer: „nie przesadzajmy, null jest literałem, takim specjalnym, ale określa pewną wartość (a dokładniej jej brak). Oczywiście lepiej jest zwracać niepuste kolekcje, ale czasem null może świadczyć np. o braku/nieistnieniu czegoś.”
Dalej w praktycznie każdej sytuacji da się go zastąpić czymś lepszym. Chciałbym zobaczyć taki praktyczny use-case null. :P

Co do porównania C++ i C# przez @tylko_prawda, to C++ powstał nie tyle z myślą o wydajności co C i klasach. C. Cholerna kompatybilność z C. ;-; Dlatego ten język nie będzie zbyt fajny, jeśli chodzi o sam kod D jest o niebo lepsze, problemem są teraz tylko API bo to strasznie młody język. No i właśnie to bardziej pasuje do porównań, D jest dużo bliżej do C# i (wg. mnie) to pierwsze jest lepsze. ;)

Swoją drogą chciałbym zobaczyć kompilator D generujący kod pod JVM… *pełne nadziei spojrzenie przez okno*

Autor edytował komentarz w dniu: 19.07.2016 01:12
djfoxer   18 #11 19.07.2016 01:39

@KoczurekK: "Chciałbym zobaczyć taki praktyczny use-case null. :P " - No pewnie, że powinno się wyeliminować użycie porównań do null, ale życie weryfikuje nasze idealistyczne poglądy :P Np. masz zewnętrzny serwis, który zwraca jakieś dane. I nic nie robi z górnolotnych idei i zwraca null gdzieś przy jakimś obiekcie, np. gdy nie ma konfiguracji. I tyle. Nie obrazisz się, nie zadzwonisz i nie pogrozisz palcem . Zrobisz if(config != null) i już. A dalej już w sowim kodzie robimy idealnie jak trzeba.

Autor edytował komentarz w dniu: 19.07.2016 01:55
KoczurekK   10 #12 19.07.2016 02:00

@djfoxer: No tak, mówię oczywiście o kodzie który sami piszemy, na wybryki innych programistów nic się niestety nie poradzi. Wydaje mi się po prostu że C# bardziej rozwija się w kierunku robienia wszystkiego za początkujących niż rzeczywiste ficzery. Ta lista ciekawostek wymięka przy tym co wprowadza C++17. Oczywiście to drugie ma inne zastosowania itp., chodzi mi jedynie o skalę wprowadzanych zmian.

Autor edytował komentarz w dniu: 19.07.2016 02:00
foreste   15 #13 19.07.2016 02:53

c#> jedyna wada jest ze jest statystyczny tylko na 1 platformę , niby są forki xamarin/mono ale to nie jest co oficjalne.

djfoxer   18 #14 19.07.2016 09:20

@KoczurekK: C# 6.0 to raczej usprawnienia, niż jakieś olbrzymie nowe ficzery, jak to miało miejsce w C# 5.0, gdy wprowadzono async/await. To było coś :)

C++17? Aż z ciekawości przejrzę, chyba, że możesz link jakiś podesłać z changelogiem z przykładami.

@foreste No nie do końca, w tamtym roku MS ogłosił powstanie .NET Core, który działa na Mac OS X i Linuxie.

wojski   7 #15 19.07.2016 10:29

@djfoxer Od kiedy udostępniono sprytne wskaźniki (smart pointers chyba od C++11) to zarządzanie pamięcią w C++ mocno już nie odbiega od tego w C#. Ten język ciągle się rozwija i wiedza ze studiów może być już mocno nieaktualna.

KoczurekK   10 #16 19.07.2016 11:04

@djfoxer: https://en.wikipedia.org/wiki/C%2B%2B17
W skrócie (te najfajniejsze):
- Zbiór any
- Obsługa fs na podstawie boost::filesystem
- Obsługa typename w szablonach (w sumie formalność, gcc już to wspiera)
- Nowe zasady dedukcji typu auto
- Anonimowe przestrzenie nazw
- Usunięcie (w końcu!) trigraphów. To ma jakąś polską nazwę ale nie pamiętam. :P

//Zapomniałbym o jednym z najlepszych. ;-;
Doszły jeszcze wielowątkowe wersje kontenerów z STL (a innych się raczej nie używa) do operowania na ogromnych zbiorach danych.

//edit2:
Przykładów jeszcze za bardzo nie ma, bo to C++17 a mamy dopiero 2016. ;) Wszystko siedzi w eksperymentalnych przestrzeniach nazw i innych dziadostwach, więc jak chcesz zobaczyć przykładowy kod to musisz pogrzebać w dokumentacji C++. Na http://en.cppreference.com/ wszystko jest ładnie zrobione, ale tak jak mówiłem – musisz ręcznie tego poszukać.

Autor edytował komentarz w dniu: 19.07.2016 11:10
KoczurekK   10 #17 19.07.2016 11:05

@wojski: Brakuje smart-tablic, sam to sobie musiałem napisać. :\

dragon321   10 #18 19.07.2016 14:50

@djfoxer: Dobrze wiedzieć. :D

Wiem, że to kwestia przyzwyczajenia, ale ja jestem już spaczony składnią C++ i Javy, wiec dlatego wydaje mi się to nienaturalne. Pewnie jakbym musiał kodować w C#, to z pewnymi bólami bym sie przestawił po jakimś czasie. :)

W sumie to rozważałem naukę C#, bo .Net rozrasta się coraz bardziej i powoli staje sie drugą Javą. Więc pewnie by się przydało go znać.

@TestamenT: Rozwiązanie w GO jeszcze bym mógł zaakceptować. Chociaż jak wspomniał djfoxer, to kwestia przyzwyczajenia.

Autor edytował komentarz w dniu: 19.07.2016 14:51
foreste   15 #19 19.07.2016 18:24

@djfoxer: ale to raczej asp net , nie typowy c# dla aplikacji okienkowych.

  #20 19.07.2016 22:17

@foreste: Dla aplikacji okienkowych masz bindingi do GTK# i Qt. Możesz też użyć przez Unity3d.

dragon321   10 #21 20.07.2016 00:37

@KoczurekK: Fajne zmiany. Zwłaszcza te wielowątkowe wersje kontenerów STL.

djfoxer   18 #22 20.07.2016 08:26

@foreste: Tak, stąd też napisałem "No nie do końca" :)