poniedziałek, 20 lipca 2020

Tuple

Temat stosowania tupli ma tylu samo przeciwników ilu zwolenników. Wraz z wejściem C# 7.0 Tuple zyskał nową składnię, która ułatwia pracę z nimi. Na początek zobaczmy co się zmieniło a później zastanówmy się kiedy ich używać.

Najprostsza postać Tupla to ta znana przed C# 7.0:

            var person = new Tuple<string, string, int>("Jacek", "Kowalski", 10);
            Console.WriteLine($"My name is {person.Item1} {person.Item2}. I'm {person.Item3} years old.");

Składnia ta nie pozwala na nazwanie poszczególnych składowych. Do poszczególnych składowych odwołujemy się poprzez składowe ItemX gdzie X to numer pola.
Zastosowanie pól numerowanych nie jest wygodnym rozwiązaniem i z pewnością nie motywowało do częstego używania Tupli. Wszystko zmieniło się wraz z C# 7.0 gdzie wprowadzono nową składnię:

            (string FirstName, string LastName, int Age) person = ("Jacek", "Kowalski", 10);
            Console.WriteLine($"My name is {person.FirstName} {person.LastName}. I'm {person.Age} years old.");

Zapis możemy skrócić korzystając z słówka var:

            var person = (FirstName: "Jacek", LastName: "Kowalski", Age: 10);
            Console.WriteLine($"My name is {person.FirstName} {person.LastName}. I'm {person.Age} years old.");

Zapis możemy jeszcze bardziej skrócić, jednak wtedy wracamy do dawnej konwencji nazywania składowych Tupla Item:

            var person = ("Jacek", "Kowalski", 10);
            Console.WriteLine($"My name is {person.Item1} {person.Item2}. I'm {person.Item3} years old.");

Warto wspomnieć jeszcze o dekonstrukcji Tupla na jego poszczególne składowe. Oczywiście do poszczególnych składowych można odnieść się po nazwie pola / ItemX. Warto jednak wiedzieć, że istnieje alternatywa:

            var (firstName, lastName, age) = person;
            Console.WriteLine($"My name is {firstName} {lastName}. I'm {age} years old.");

Teraz zastanówmy się nad tym kiedy używać Tupli a kiedy nie. Zacznijmy od kilku ich właściwości, które mogą pomóc zdecydować czy je używać czy nie:
  1. Zdefiniowane nazwy składowych Tupla nie są stałe - podczas działania programu znikają.
  2. Nowy Tupl to instancja struktury ValueTuple<T> - typ wartościowy, więc przekazywany jest przez wartość. Problem częstego kopiowania można zaadresować przez zastosowanie ref local / ref return
  3. Tuple pozwalają na porównywanie za pomocą == czy !=

Kiedy więc warto skorzystać z Tupli? Z pewności w metodach prywatnych biblioteki czy klasy. Pozwala to uniknąć tworzenia kolejnych definicji obiektów. W przypadku publicznych API nie jest zalecane używanie Tupli.