c# 7.0 – operator Is, wzorce rekurencyjne

Dzisiaj przyszedł czas na wzorzec agregujący w pewien sposób wszystkie poprzednie, a mianowicie wzorzec rekurencyjny. Załóżmy, że mamy następującą hierarchię klas, np.:

    public class Employee
    {
        public string Name { get; set; }
        public object Salary { get; set; }
    }

    public class Manager : Employee
    {
        public object DirectReports { get; set; }
    }

W poprzednich wersjach C#, było możliwe jedynie sprawdzenie czy obiekt jest typu “Manager” czy innej implementacji. W wersji 7.0 możemy:

            object employee = new Manager()
            {
                Salary = 53,
                DirectReports = new Employee[5]
            };

            if (employee is Manager { Salary is int salary,DirectReports is Employee[] directReports})
            {
                Console.WriteLine("It's a manager with salary {0} and direct reports {1}", salary, directReports.Length);
            }

Widzimy tutaj wzorzec rekurencyjny składający się z:

  • type pattern – sprawdzamy czy obiekt jest typu Manager
  • zagnieżdżonego type pattern – sprawdzamy czy Salary jest liczbą całkowitą
  • zagnieżdżonego type pattern – sprawdzamy czy DirectReports jest tablicą.

Jeśli któryś z warunków jest nieprawdziwy, całość zwróci false. Jeśli np. zamienimy tablicę na kolekcję, wtedy na ekranie nic się nie wyświetli:

        private static void Main(string[] args)
        {
            object employee = new Manager()
            {
                Salary = 53,
                DirectReports = new List<Employee>()
            };

            if (employee is Manager { Salary is int salary,DirectReports is Employee[] directReports})
            {
                Console.WriteLine("It's manager with salary {0} and direct reports {1}", salary, directReports.Length);
            }
        }

Można również umieścić “constant pattern”, o którym pisałem w poprzednim poście:

            object employee = new Manager()
            {
                Name="Piotr"
            };

            if (employee is Manager { Name is "Piotr"})
            {
                Console.WriteLine("Name: Piotr");
            }

W tym przypadku, instrukcja zwróci true, gdy właściwość Name ma wartość “Piotr”. Constant pattern, nie wyodrębnia zawartości, jedynie sprawdza czy zmienna jest równa danej stałej.

Zmieńmy teraz trochę strukturę klas na:

    public class Employee
    {
        public string Name { get; set; }
    }

    public class Manager : Employee
    {
        public object DirectReport { get; set; }
    }

Za pomocą wzorca możemy rekurencyjnie sprawdzać właściwości o dowolnym zagnieżdżeniu, tzn.:

            object employee = new Manager()
            {
                DirectReport = new Employee { Name = "Piotr" }
            };

            if (employee is Manager { DirectReport is Employee { Name is string name}})
            {
                Console.WriteLine(name);
            }

Rekurencyjna jest zatem przerywana, gdy jakiś warunek zwraca false. Jeśli zwraca true, wtedy kolejne warunki będą sprawdzane, przeglądając graf obiektów o dowolnej głębokości. W powyższym przykładzie, zatem musimy mieć obiekt typu Manager, w którym DirectReport jest typu Employee, a właściwość Name jest napisem.

Tak jak zwykle, na zakończenie zobaczmy kod po kompilacji:

		private static void Main(string[] args)
		{
			object employee = new Manager
			{
				Salary = 53,
				DirectReports = new List&lt;Employee&gt;()
			};
			Manager manager = employee as Manager;
			int salary;
			Employee[] directReports;
			bool arg_65_0;
			if (manager != null)
			{
				int? num = manager.Salary as int?;
				salary = num.GetValueOrDefault();
				if (num.HasValue)
				{
					directReports = (manager.DirectReports as Employee[]);
					arg_65_0 = (directReports != null);
					goto IL_65;
				}
			}
			arg_65_0 = false;
			IL_65:
			bool flag = arg_65_0;
			if (flag)
			{
				Console.WriteLine("It's manager with salary {0} and direct reports {1}", salary, directReports.Length);
			}
		}

Wyraźnie widzimy, że wszystkie warunki muszą zostać spełnione tzn.:

  • Employee musi być typu Manager
  • Salary jest liczbą całkowitą
  • DirectReports jest tablicą Employee

Ponadto, jak widzimy z powyższego kodu, typy proste mogą być nullable, o ile mają w sobie jakąś wartość. Zatem salary mogłaby być typu int?, o ile wartość nie jest NULL.

4 thoughts on “c# 7.0 – operator Is, wzorce rekurencyjne”

  1. @rbl
    Zwięzłości i czytelności kodu. Wszystko to możesz osiągnąć także if’ami ale traci kod na czytelności.

  2. Pytanie gdzie taki kod bedzie przydatny. Wydaje mi sie ze ten feature jest dodawany ze zwgledu na integracje tupli w C# i nie powinien byc uzywany poza nimi.

Leave a Reply

Your email address will not be published.