Dzisiaj kolejna nowa funkcjonalność w c#. Zacznijmy od przykładu:
public class Point { public double Dist => Math.Sqrt(X * X + Y * Y); public double X; public double Y; }
X oraz Y to zwykłe pola (tak nie powinno się ich definiować jako publiczne ale to tylko przykład). Następnie Dist to dziwny twór… Wiemy, że mamy tam wyrażenie lambda, które wywołuje Math.Sqrt i robi obliczenia. Zobaczymy jak możemy z tego skorzystać w kodzie:
Point point = new Point(); point.X = 5; point.Y = 6; double dist = point.Dist; Console.WriteLine(dist);
Dist to po prostu zwykła właściwość. Zaglądając do IL na zdekompilowany kod c# ujrzyjmy:
public class Point { public double X; public double Y; public double Dist { get { return Math.Sqrt(this.X * this.X + this.Y * this.Y); } } }
Dla tych co preferują IL:
.class public auto ansi beforefieldinit ConsoleApplication2.Point extends [mscorlib]System.Object { // Fields .field public float64 X .field public float64 Y // Methods .method public hidebysig specialname instance float64 get_Dist () cil managed { // Method begins at RVA 0x2050 // Code size 33 (0x21) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldfld float64 ConsoleApplication2.Point::X IL_0006: ldarg.0 IL_0007: ldfld float64 ConsoleApplication2.Point::X IL_000c: mul IL_000d: ldarg.0 IL_000e: ldfld float64 ConsoleApplication2.Point::Y IL_0013: ldarg.0 IL_0014: ldfld float64 ConsoleApplication2.Point::Y IL_0019: mul IL_001a: add IL_001b: call float64 [mscorlib]System.Math::Sqrt(float64) IL_0020: ret } // end of method Point::get_Dist .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2072 // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Point::.ctor // Properties .property instance float64 Dist() { .get instance float64 ConsoleApplication2.Point::get_Dist() } } // end of class ConsoleApplication2.Point
Nie lubię tej składni ponieważ wyobraźmy sobie, że mamy:
public class Point { public double GetDist => Math.Sqrt(X * X + Y * Y); public double X; public double Y; }
Z punktu czytelnika ma to sens. GetDist i wyrażenie lambda. Kompilator jednak nie wygeneruje metody i poniższy kod zakończy się błędem kompilacji:
Point point = new Point(); point.X = 5; point.Y = 6; double dist = point.GetDist(); Console.WriteLine(dist);
Z kolei bardzo podoba konstrukcja poskutkuje wygenerowaniem metody:
public class Point { public double GetDist()=> X*Y; public double X; public double Y; } class Program { static void Main(string[] args) { Point point = new Point(); point.X = 5; point.Y = 6; double dist = point.GetDist(); Console.WriteLine(dist); } }
Zdekompilowany kod:
public class Point { public double X; public double Y; public double GetDist() { return this.X * this.Y; } }
Możemy również w podobny sposób dodawać parametry tzn.:
public class Point { public double GetDist(int z)=> X*Y*z; public double X; public double Y; } class Program { static void Main(string[] args) { Point point = new Point(); point.X = 5; point.Y = 6; double dist = point.GetDist(3); Console.WriteLine(dist); } }
To już wygeneruje normalną metodę:
public class Point { public double X; public double Y; public double GetDist(int z) { return this.X * this.Y * (double)z; } }
Rozwiązane fajne, ale należy stosować z rozwagą. Szczególnie nie podoba mi się, że nie jest to jasne kiedy właściwość, a kiedy metoda zostanie wygenerowana. Moim zdaniem nie wynika to wystarczająco z samej składni.
Moim zdaniem po samym nawiasie można odczytać czy to metoda czy właściwość. Nie wspominając o trzymaniu się konwencji.
Składnia bardzo ładna.
Popieram Arka, składnia jest dobra i czytelna.