Wprowadzenie

W dzisiejszym artykule skupimy się na opisaniu nowości w języku C# w wersji 6.0. Bez omawiania historii przechodzimy do najnowszej wersji języka, która w poniższych porównaniach zostanie zestawiona ze starszymi wersjami.

Typy statyczne w bloku using

Wszyscy jesteśmy dobrze zaznajomieni z pojęciem statycznego członka klasy. Najprosty przykład to wywołanie metody WriteLine(...) z klasy Console. Od wersji 6.0 nie musimy tego robić w znany wszystkim sposób. Możemy zaimportować statyczny typ.

Przed C# 6.0:

using System;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Console.WriteLine("Witaj Drogi Czytelniku!");
		}
	}
}

C# 6.0:

using System;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			WriteLine("Witaj Drogi Czytelniku!");
		}
	}
}

Formatowanie łańcuchów tekstowych

Możecie zapomnieć o formatowaniu łańuchów tekstowych z klamrami {0}, aby następnie zamienić je na rzeczywiste wartości. C# 6.0 ma nową funkcję pod nazwą interpolacji ciągu (string interpolation) dzięki której można teraz bezpośrednio zapisywać swoje argumenty w formatowanym ciągu.

Przed C# 6.0:

using System;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			string car = "Audi";
			string model = "RS6 C7";
			WriteLine("Sportowe kombi z rodziny {0} to model {1}.", car, model);
			ReadKey();
		}
	}
}

C# 6.0:

using System;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			string car = "Audi";
			string model = "RS6 C7";
			WriteLine($"Sportowe kombi z rodziny {car} to model {model}.");
			ReadKey();
		}
	}
}

W tak formatowanym tekście można nawet wprowadzić instrukcje warunkowe:

using System;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			string car = "Audi";
			string model = "RS6 C7";
			int priceFrom = 539000;
			WriteLine($"Sportowe kombi z rodziny {car} to model {model}.");
			WriteLine($"Wartość: {(priceFrom > 100000 ? "drogie autko" : "tanie auto")}");
			ReadKey();
		}
	}
}

Inicjowanie słowników

Nowa wersja języka zmienia podejście do inicjowania słowników. W poprzednich wersjach proces ten odbywa się na zasadzie deklaracji pary {„klucz”, „wartość”}. W obecnej wersji klucz może być umieszczony w nawiasach kwadratowych [„Klucz”] a następnie możemy do niego przypiasać wartość: [„Klucz”] = wartość;. Nowa składnia jest bardziej czytelna i przejrzysta.

Przed C# 6.0:

using System;
using System.Collections.Generic;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Dictionary<int, string> data;
			data = new Dictionary<int, string>()
			{
				{1, "Audi" },
				{2, "Porsche"},
				{3, "Maserati" }
			};
			foreach (var item in data)
			{
				WriteLine(item.Key + " : " + item.Value);
			}
			ReadKey();
		}
	}
}

C# 6.0:

using System;
using System.Collections.Generic;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Dictionary<int, string> data;
			data = new Dictionary<int, string>()
			{
				[1] = "Audi",
				[2] = "Porsche",
				[3] = "Maserati"
			};
			foreach (var item in data)
			{
				WriteLine($"{item.Key} : {item.Value}");
			}
			ReadKey();
		}
	}
}

Automatyczne inicjowanie właściwości

C# 6.0 pokazuje nową koncepcję inicjowania właściwości – zamiast wykonywania tego procesu w konstuktorze. Kolejną wartą uwagi nowością jest możliwość zadeklarowania samego gettera - dzięki temu setter zostanie ustawiony jako prywatny i użytkownik nie będzie miał do niego dostępu.

Przed C# 6.0:

using System;
using System.Collections.Generic;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			CarClass car = new CarClass();
			WriteLine("Marka: {0}, Model: {1}", car.Brand, car.Model);
			ReadKey();
		}
	}
	class CarClass
	{
		public string Brand { get; set; }
		public string Model { get; set; }
		public CarClass()
		{
			Brand = "Audi";
			Model = "RS6 C7";
		}
	}
}

C# 6.0:

using System;
using System.Collections.Generic;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			CarClass car = new CarClass();
			WriteLine($"Marka: {car.Brand}, Model: {car.Model}");
			ReadKey();
		}
	}
	class CarClass
	{
		public string Brand { get; } = "Audi";
		public string Model { get; set; } = "RS6 C7";
	}
}

Wyrażenie nameof

Wyrażenie to przychodzi nam z pomocą, gdy zaczynamy myśleć o refractoringu naszego kodu. Wyobraźmy sobie sytuację w której sprawdzamy wartość jakiegoś parametru a następnie wyświetlamy błąd związany z tym parametrem – hardcoded string. Jeżeli teraz zmienimy nazwę naszego parametru nie wpłynie to nasz komunikat błędu.

Przed C# 6.0:

using System;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			DoSomething();
		}
		private static void DoSomething()
		{
			int? x = null;
			if(x==null)
			{
				// x jest nazwą typu. Co jeżeli ktoś zmieni nazwę?
				// Komunikat błędu będzie nieodpowiedni.
				throw new Exception("x is null");
			}
		}
	}
}

C# 6.0:

using System;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			DoSomething();
		}
		private static void DoSomething()
		{
			int? number = null;
			if(number==null)
			{
				throw new Exception(nameof(number) + " is null");
			}
		}
	}
}

Operator warunkowy null

W nowej wersji języka C# pojawia się nowa konepcja operatowa warunkowego null, która pozowala sprawdzić czy instacja obiektu jest pusta czy też nie bez wprowadzania instrukcji warunkowej. Składania wyrażenia nie jest skomplikowana - ?. wyrażenie pozwala na sprawdzenie czy instacja jest pusta czy nie, jeżeli nie wykonywany jest kod tego wyrażenia, w przeciwnym wypadku wykonywany jest kod po wyrażeniu ??. Spójrz na poniższe przykłady.

Przed C# 6.0:

using System;
using System.Threading.Tasks;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Car car = new Car();
			if(car.Brand==String.Empty)
			{
				car = null;
			}
			// sprawdzenie po staramu
			WriteLine(car != null ? car.Brand : "Brak definicji");
			ReadKey();
		}
	}
	public class Car
	{
		public string Brand { get; set; } = "";
	}
}

C# 6.0:

using System;
using System.Threading.Tasks;
using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Car car = new Car();
			if(car.Brand==String.Empty)
			{
				car = null;
			}
			// nowy operator null
			// jeżeli wyrażenie jest prawdziwe zostanie wykonany blok kodu po lewej stronie
			// jeżeli nie - blok kodu po prawej stronie
			WriteLine(car?.Brand ?? "Brak definicji");
			ReadKey();
		}
	}
	public class Car
	{
		public string Brand { get; set; } = "";
	}
}

Właściwości i metody bez ciała

Bardzo dużym udogodnieniem jest możliwość pisania metod i właściwości bez ich caiała. W takim wypadku należy użyć wyrażenia lambda i można przystąpić do pisania właściwego kodu.

C# 6.0 (metoda):

using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			double x = 32.21;
			double y = 54.32;
			WriteLine(AddNumber(x, y));
			ReadKey();
		}
		static double AddNumber(double x, double y) => x + y;
	}
}

C# 6.0 (właściwość)

using static System.Console;
namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Car car = new Car();
			WriteLine(car.FullName);
			ReadKey();
		}
	}
	public class Car
	{
		public string Brand { get; } = "Audi";
		public string Model { get; } = "RS6";
		public string FullName => Brand + " " + Model;
 
	}
}