Utilizzare le Value Tuple

.NET C# ValueTuple

Tutti noi scriviamo metodi che restituiscono un valore, un metodo string restituisce una stringa, un metodo int restituisce un int, ma cosa fai quando hai bisogno di restituire più di un valore? Naturalmente, potresti creare un contenitore come class/struct, tipo anonimo o utilizzare parametri out.

L’utilizzo di tipi anonimi fornisce un modo per restituire un insieme di proprietà utilizzando un singolo oggetto senza dover definire esplicitamente un tipo. Non c’è supporto da intellisense, devi ricordare il nome e il tipo della proprietà e anche il compilatore non può trovare errori se accidentalmente hai scambiato i campi.

La soluzione con i parametri out non incapsula i risultati in un singolo oggetto restituito, è un mix di parametri per valore e per riferimento (out). È una soluzione ragionevole ma non dovrebbe essere abusata.

Le classi aiutano in questi casi e sono ampiamente utilizzate in tutte le applicazioni, ma può accadere che finisci per usarle per tutto, anche quando la complessità è bassa, nuove classi vengono create aggiungendo piccoli pezzi che differiscono solo in pochi attributi o, in alcuni casi, sono una semplice composizione di diverse entità. Questo influisce sulla codebase, specialmente quando devi raggruppare elementi eterogenei.

Microsoft ha introdotto un nuovo modo, prima con le Tuple e poi con le ValueTuple in C# 7, per supportare gli sviluppatori in questi scenari. Se stai usando quella versione o successiva, puoi utilizzarle installando System.ValueTuple dal pacchetto NuGet.

Le Tuple sono ottime per spostare dati permettendoti di legare insieme elementi che potrebbero essere di tipi diversi, per esempio, se voglio restituire alcune proprietà dell’entità Currency da un metodo senza restituire l’intera istanza, potrei fare così:

public class Currency
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }

    [...]
}

internal class Program
{
    static void Main(string[] args)
    {
        // Non solo assegnare variabili multiple, ma dichiararle anche.
        var (name, isActive) = GetSomeDataFromCurrency(int currencyId);

        System.Console.WriteLine($"La valuta {name} ha stato {isActive}.");
    }

    static (string Name, bool IsActive) GetSomeDataFromCurrency(int id)
    {
        // Ottieni la Currency, fai qualche operazione e restituisci i valori
        return (currency.Name, currency.IsActive);
    }
}

Ma penso che siano super utili per combinare valori che non hanno molto in comune tra loro senza creare piccole classi personalizzate ogni volta (usate solo in quel punto) o usare uno o più parametri out. Per esempio, partendo da oggetti Country e Currency che hanno un mucchio di proprietà, come definito di seguito:

public class Currency
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }

    [...]
}

public class Country
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Language { get; set; }
    public int Population { get; set; }

    [...]
}

Per qualche motivo ho bisogno di combinare i due oggetti e come mostra l’esempio qui sotto, il metodo DoSomeWithCountryAndCurrency ha i tipi degli elementi restituiti che appaiono in un insieme di parentesi, sia nella dichiarazione del metodo che come parte dell’istruzione return. La Tuple ha tre elementi, i valori CountryName, Population e CurrencyName e sono ordinati. Puoi destrutturare un’istanza Tuple in variabili separate, o in altri modi (le tuple hanno diverse altre possibilità sintattiche aggiuntive).

internal class Program
{
    static void Main(string[] args)
    {
        // Non solo assegnare variabili multiple, ma dichiararle anche.
        var (countryName, population, currencyName) = DoSomeWithCountryAndCurrency(currencyId, countryId);

        System.Console.WriteLine($"{countryName} ha {population} cittadini e ha {currency} come valuta.");
    }

    static (string CountryName, int Population, string CurrencyName) DoSomeWithCountryAndCurrency(int country, int currency)
    {        
        // Fai qualche operazione e restituisci i valori
        return (country.Name, country.Population, currency.Name);
    }
}

Conclusione

Le Tuple sono una sequenza ordinata di elementi (eterogenei) e come detto prima vengono utilizzate quando un metodo deve restituire più di un valore. Fondamentalmente, è un modo comodo per gestire i dati in situazioni in cui costruire una classe personalizzata è eccessivo. Dovresti considerare un approccio basato su Tuple, mi sembra un approccio molto flessibile e può fornire una varietà di utilizzi.

Buon coding!

Alberto