piątek, 21 sierpnia 2020

Modyfikowanie pola klasy, które jest strukturą

W jednym z ostatnich postów pisałem o użyciu słowa kluczowego ref w kontekście metod, pól i właściwości. Dzisiaj coś praktycznego, aczkolwiek raczej mało spotykanego zachowania. Zobaczmy na poniższy przykład:

    class Program
    {
        static void Main(string[] args)
        {
            var rectangle = new Rectangle(new Point3D(1, 2, 3));
            rectangle.A.X = 10; //Error CS1612  Cannot modify the return value of 'Rectangle.A' because it is not a variable
        }
    }

    public class Rectangle
    {
        public Point3D A { get; }

        public Rectangle(Point3D a)
        {
            A = a;
        }
    }

    public struct Point3D
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        public double DoSomeCalculation()
        {
            return X * Y * Z;
        }
    }

Klasa Rectangle ma pole typu struct Point3D. Pola struktury Point3D pozwalają na zmianę ich wartości (posiadają settery). W metodzie Main tworzymy obiekt klasy Rectangle i chcemy zmodyfikować wartość właściwości X struktury. Niestety kompilator na to nie pozwala. Dlaczego?
Właściwości to nic innego niż pole plus metody get/set - dlatego właśnie kompilator nie pozwala na przypisanie wartości do pola. Wywołując metodę tak na prawdę tworzymy kopię której wartość została by zmieniona. Kompilator nie pozwala nam zrobić takiego błędu.

Pozostaje pytanie w jaki sposób więc umożliwić zmianę wartości tego pola? Możemy zastosować słowo kluczowe ref:

    class Program
    {
        static void Main(string[] args)
        {
            var rectangle = new Rectangle(new Point3D(1, 2, 3));
            rectangle.A.X = 10; //Error CS1612  Cannot modify the return value of 'Rectangle.A' because it is not a variable
            Console.WriteLine(rectangle.A.X);
        }
    }

    public class Rectangle
    {
        private Point3D _a;
        public ref Point3D A => ref _a;

        public Rectangle(Point3D a)
        {
            _a = a;
        }
    }

    public struct Point3D
    {
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }

        public Point3D(double x, double y, double z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        public double DoSomeCalculation()
        {
            return X * Y * Z;
        } 
} 

Brak komentarzy:

Prześlij komentarz