// ========================================================================
//
// Copyright (C) 2017-2018 MARC CHEVALDONNE
//                         marc.chevaldonne.free.fr
//
// Module        : Program.CSharp7.cs
// Author        : Marc Chevaldonné
// Creation date : 2017-09-19
//
// ========================================================================

using System;
using static System.Console;

namespace ex_004_009_Tuple
{
    partial class Program
    {
        /// <summary>
        /// Le constat sur l'utilisation des Tuples jusqu'à C# 6.0 est le suivant :
        ///     - l'utilisation des paramètres out (a priori rien à voir avec les Tuples), n'est pas pratique et lourd et ils ne marchent pas avec les méthodes asynchrones
        ///     - l'utilisation des Tuples est verbeuse et l'allocation est lourde
        ///     - ceci entraine beaucoup d'écriture de code pour des variables à durée de vie parfois très limitée
        ///     
        /// C# 7.0 ajoute donc les types Tuple et des sucres syntaxiques pour améliorer tout ceci ... :
        /// </summary>
        static void NouveautésCSharp7()
        {
            //La méthode suivante rend directement trois variables en un "tuple value" : un string, et deux int
            //ATTENTION : ceci n'est utilisable que si vous ajoutez le package NuGet System.TupleValue
            (string, int, int) NounoursData(Nounours nounours)
            {
                return (nounours.Nom, nounours.NbPoils, nounours.Naissance.Year);
            }
            //pour utiliser le résultat de la méthode ci-dessous, vous pouvez tout stocker dans une seule variable var (de type Tuple<string, int, int> ici) :
            var test = NounoursData(new Nounours { Nom = "Gizmo", NbPoils = 123456789, Naissance = new DateTime(1984, 6, 8) });
            //et accéder aux différentes valeurs avec Item1, Item2, Item3...
            WriteLine($"{test.Item1} ; {test.Item2} ; {test.Item3}");

            //un des problèmes des Tuples, c'est que Item1, Item2, Item3... ce n'est pas très parlant. On peut donc réécrire la méthode
            //de la manière suivante, en donnant un nom aux valeurs de retour :
            (string nom, int nbPoils, int annéeDeNaissance) NounoursData2(Nounours nounours)
            {
                return (nounours.Nom, nounours.NbPoils, nounours.Naissance.Year);
                //cette dernière ligne peut aussi s'écrire :
                //return (nom: nounours.Nom, nbPoils: nounours.NbPoils, annéeDeNaissance: nounours.Naissance.Year);
                //au cas où vous ne calculeriez pas les éléments dans le bon ordre
            }
            //l'accès aux résultats reste inchangé :
            var test2 = NounoursData2(new Nounours { Nom = "Gizmo", NbPoils = 123456789, Naissance = new DateTime(1984, 6, 8) });
            //par contre, on n'utilise plus Item1, Item2, Item3, mais les noms utilisés en retour de la méthode (ici nom, nbPoils et annéeDeNaissance)
            WriteLine($"{test2.nom} ; {test2.nbPoils} ; {test2.annéeDeNaissance}");

            //notez que les noms n'ont pas d'autre intérêt que de simplifier l'utilisation. C'est le type des éléments des Tuples qui permet de comparer deux Tuples.


            //DECONSTRUCTION
            //La deconstruction est une syntaxe pour diviser un tuple (ou un autre type) en ses différents éléments en de nouvelles variables individuelles
            //ci-dessous, au lieu de stocker le retour de la méthode NounoursData précédente dans un Tuple, on le stocker dans une Tuple "déconstruit"
            (string nom, int nbPoils, int année) = NounoursData(new Nounours { Nom = "Gizmo", NbPoils = 123456789, Naissance = new DateTime(1984, 6, 8) });
            WriteLine($"{nom} ; {nbPoils} ; {année}");

            //on peut aussi utiliser var comme d'habitude
            (var nom2, var nbPoils2, var année2) = NounoursData(new Nounours { Nom = "Gizmo", NbPoils = 123456789, Naissance = new DateTime(1984, 6, 8) });
            WriteLine($"{nom2} ; {nbPoils2} ; {année2}");

            //ou bien utiliser var devant le tuple literal
            var (nom3, nbPoils3, année3) = NounoursData(new Nounours { Nom = "Gizmo", NbPoils = 123456789, Naissance = new DateTime(1984, 6, 8) });
            WriteLine($"{nom3} ; {nbPoils3} ; {année3}");

            //ou encore déconstruire dans des variables existantes :
            string nom4;
            int nbPoils4, année4;
            (nom4, nbPoils4, année4) = NounoursData(new Nounours { Nom = "Gizmo", NbPoils = 123456789, Naissance = new DateTime(1984, 6, 8) });
            WriteLine($"{nom4} ; {nbPoils4} ; {année4}");

            //Notez qu'on peut déconstruire autre chose que des Tuples (cf. ex_
        }
    }

    class Nounours
    {
        public string Nom { get; set; }
        public int NbPoils { get; set; }
        public DateTime Naissance { get; set; }

        public override string ToString()
        {
            return $"{Nom} a {NbPoils} poils et est né en {Naissance.Year}";
        }
    }
}
