Przejdź do głównej zawartości

[C#] Czym są ValueTuple i jak ich używać?


Od C# 7 (.NET 4.7) dostepna jest nowa klasa - ValueTuple. Jest ona alternatywą dla klasy Tuple, o której pisałam tutaj.

Podstawowe różnice między ValueTuple a Tuple:
  • ValueTuple to value type, zaś Tuple to reference type (więcej o różnicy tutaj)
  • ValueTuple można stworzyć za pomocą prostej składni z nawiasami
  • ValueTuple może zawierać w sobie więcej niż 8 elementów
  • ValueTuple może mieć nazwane elementy
  • ValueTuple może zostać zdekonstruowany do pojedynczych zmiennych
Przyjrzyjmy się bliżej nowym cechom ValueTuple.

ValueTuple można stworzyć na następujące sposoby:

Wprost sprecyzować typ:

ValueTuple<int, string,string> person = (1, "Dziki", "Bill");

Możemy darować sobie napisanie "ValueTuple":

(int, string,string) person = (1, "Dziki", "Bill");

Albo w ogóle pozwolić kompilatorowi samemu wywnioskować typ na podstawie parametrów:

var person = (1, "Dziki", "Bill");

Uwaga. Chcąc utworzyć ValueTuple z tylko jednym elementem (ciężko mi wyobrazić sobie po co, ale do odważnych świat należy) musimy zastosować składnię:

ValueTuple<int> number = new ValueTuple<int>(2);

Napisanie...

var number = (1);

...utworzy inta, a nie ValueTuple.

ValueTuple może zawierać więcej niż 8 elementów:

var data = (1, 2, 3, 4, 5, 6, 7, 8, 9); 

Wciąż do elementów można dostać się poprzez pola Item1, Item2 itd:

var data = (1, 2, 3, 4, 5, 6, 7, 8, 9); 
var first = data.Item1;

Na szczęście w ValueTuple możemy nazywać poszczególne elementy:

(int Id, string FirstName, string LastName) person = (1, "Dziki", "Bill")

Możemy je nazwać także po prawej stronie przypisania:

var person = (Id: 1, FirstName: "Dziki", LastName: "Bill")

Wtedy użycie takiego obiektu zaczyna wyglądać trochę bardziej sensownie, niż miało to miejsce przy Tuple:

var person = (Id: 1, FirstName: "Dziki", LastName: "Bill");

var firstName = person.FirstName;

Przekazując ValueTuple przez parametr, musimy pamiętać o specyficznej składni z nawiasami:

void PrintPersonTuple((int, string, string) person)
{
}

Podobnej składni użyjemy zwracając ValueTuple z metody:

(int, string, string) GetPerson()
{
    return (Id:1, FirstName: "Dziki", LastName: "Bill");
}

Przyjrzyjmy się jeszcze dekonstrukcji ValueTuple. Jest to mechanizm pozwalający nam wydobyć poszczególne elementy z ValueTuple i przypisać je do zmiennych:

(int personId, string personName, string personLastName) = GetPerson();

(int, string, string) GetPerson()
{
    return (Id:1, FirstName: "Dziki", LastName: "Bill");
}

Jeśli nie chcemy użyć któregoś z pól ValueTuple, możemy użyć następującej składni:

(int personId, _, string personLastName) = GetPerson();

(int, string, string) GetPerson()
{
    return (Id:1, FirstName: "Dziki", LastName: "Bill");
}

Na koniec nasuwa się takie samo pytanie, jakie pojawiło się przy zwykłych Tuple: kiedy należy ich używać? Moim zdaniem mimo że ValueTuple rozwiązały część problemów, jakie dotyczyły Tuple, wciąż w większości przypadków lepiej użyć klasy lub struktury do agregowania różnych informacji w spójną całość. Więcej o zasadności użycia Tuple można znaleźć pod koniec tego posta.

Komentarze