C# 6.0: Definiowanie metod za pomocą wyrażenia lambda

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.

2 thoughts on “C# 6.0: Definiowanie metod za pomocą wyrażenia lambda”

  1. 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.

Leave a Reply

Your email address will not be published.