Update [16.10.2014]: Niestety opisana konstrukcja prawdopodobnie nie znajdzie się w finalnej wersji C# 6.0. Możliwe, że wcześniej czy później zostanie to zaimplementowane, ale wiadomo, różnie z tym bywa.
Czas zacząć pisać o nowym c#. Najpierw pobierzmy wersję CTP z:
http://www.visualstudio.com/en-us/downloads/visual-studio-14-ctp-vs.aspx
Oczywiście to preview, więc odradzam instalowanie tego w pracy, chyba, że na VM. Lepiej nie ryzykować re-instalacją wszystkiego od nowa.
Często w kodzie można spotkać następujący wzorzec:
class Person { private readonly string _firstName; private readonly string _lastName; public Person(string firstName,string lastName) { _firstName = firstName; _lastName = lastName; } }
Kod często praktykowany, ale trochę nadmiarowy. Musimy zdefiniować konstruktor, tylko po to aby zainicjalizować kilka zmiennych. W C# do dyspozycji będziemy mieli następującą konstrukcję:
class Person(string firstName,string lastName) { private readonly string _firstName=firstName; private readonly string _lastName = lastName; public override string ToString() { return string.Format("{0} {1}", _firstName, _lastName); } }
Konstrukcja przydatna, aczkolwiek trochę dziwna. Nic takiego nie mieliśmy w CPP czy w Javie. Myślę jednak, że można przyzwyczaić się i jest to po prostu praktyczne.
Jest to najzwyklejszy konstruktor i Person zainicjalizujemy następująco:
Person person = new Person("piotr","zielinski"); Console.WriteLine(person.ToString());
Jeśli jednak będziemy chcieli skompilować to, dostaniemy błąd:
Error 1 Feature 'primary constructor' is only available in 'experimental' language version. c:\ConsoleApplication2\ConsoleApplication2\Program.cs 17 21 ConsoleApplication2
Nic dziwnego. To tylko preview, nie ma pewności nawet czy ta konstrukcja będzie w oficjalnej wersji. Jeśli jednak chcemy to odpalić, przejdźmy do edycji pliku projektu i dodajmy tag LangVersion z ustawioną wartością “Experimental”:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>bin\Debug\</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <LangVersion>Experimental</LangVersion> </PropertyGroup>
Próba użycia konstruktora bezparametrowego zakończy się również błędem:
Person person = new Person();
Tak jak wcześniej, zdefiniowanie niestandardowego konstruktora powoduje, że domyślny nie jest już automatycznie generowany.
Nic nie stoi na przeszkodzie, abyśmy pomieszali konstruktory:
class Person(string firstName,string lastName) { private readonly string _firstName=firstName; private readonly string _lastName = lastName; private int _age; public Person(string firstName,string lastName,int age) :this(firstName,lastName) { _age = age; } }
Oczywiście możemy zdefiniować wyłącznie jeden primary constructor.
Zajrzyjmy również do IL:
.class nested private auto ansi beforefieldinit Person extends [mscorlib]System.Object { // Fields .field private initonly string _firstName .field private initonly string _lastName .field private int32 _age // Methods .method public hidebysig specialname rtspecialname instance void .ctor ( string firstName, string lastName ) cil managed { // Method begins at RVA 0x2067 // Code size 22 (0x16) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld string ConsoleApplication2.Program/Person::_firstName IL_0007: ldarg.0 IL_0008: ldarg.2 IL_0009: stfld string ConsoleApplication2.Program/Person::_lastName IL_000e: ldarg.0 IL_000f: call instance void [mscorlib]System.Object::.ctor() IL_0014: nop IL_0015: ret } // end of method Person::.ctor .method public hidebysig specialname rtspecialname instance void .ctor ( string firstName, string lastName, int32 age ) cil managed { // Method begins at RVA 0x207e // Code size 18 (0x12) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: ldarg.2 IL_0003: call instance void ConsoleApplication2.Program/Person::.ctor(string, string) IL_0008: nop IL_0009: nop IL_000a: ldarg.0 IL_000b: ldarg.3 IL_000c: stfld int32 ConsoleApplication2.Program/Person::_age IL_0011: ret } // end of method Person::.ctor } // end of class Person
Tutaj nic nie zmieniło się. Konstruktor został po prostu wygenerowany w klasyczny sposób.
Fajnie tylko niestety nieaktualne przynajmniej do wersji C# 7.0
https://roslyn.codeplex.com/discussions/568820
Primary constructor został wyrzucony z C# 6.0
https://roslyn.codeplex.com/discussions/568820
Nie będzie Primary Constructors oraz Declaration Expressions w c# 6.0. Info za http://roslyn.codeplex.com/discussions/568820.
Mysle ze warto wspomniec, ze ta funkcja bedzie usunieta z c# 6.0 w zwiazku z innymi problemami z przeciagajacym sie Roslynem:
https://roslyn.codeplex.com/discussions/568820
The features we are cutting are:
* Primary constructors in C# (along with initializers in structs)
* Declaration expressions in C# / Out parameters in VB