web-dev-qa-db-fra.com

Est-ce que C # 7 a une déstructuration de tableau/énumérable?

En Javascript ES6, vous pouvez déstructurer des tableaux comme ceci:

const [a,b,...rest] = someArray;

a est le premier élément du tableau, b est le deuxième et rest est un tableau avec les éléments restants.

Je sais qu'en C # 7, vous pouvez déstructurer des n-uplets pendant l'affectation, mais rien ne se trouve lié à la déstructuration des tableaux/enumérables comme ceci:

var (a,b) = someTuple;

J'ai un IEnumerable où j'ai besoin des premier et deuxième éléments en tant que variables, et du reste des éléments en tant qu'autre IEnumerable. J'ai une solution, mais je pense que la déstructuration semblera plus propre.

22
kmc059000

Non, il n'y en a pas. Cependant, il n'est pas très difficile de faire quelque chose de similaire.

Qu'en est-il d'une méthode d'extension comme celle-ci:

public static class EX
{
    public static void Destructure<T>(this T[] items, out T t0)
    {
        t0 = items.Length > 0 ? items[0] : default(T);
    }

    public static void Destructure<T>(this T[] items, out T t0, out T t1)
    {
        t0 = items.Length > 0 ? items[0] : default(T);
        t1 = items.Length > 1 ? items[1] : default(T);
    }
}

Et vous pouvez l'utiliser comme ceci:

int[] items = { 1, 2 };

items.Destructure(out int t0);

L'inconvénient est qu'il vous faut une méthode d'extension par nombre d'éléments à renvoyer. Donc, si vous avez plusieurs variables à renvoyer, cette méthode risque de ne pas être très utile.

Notez que j’ai omis de vérifier la longueur et les éléments connexes, mais vous comprenez ce qui doit être fait, je suppose.

7
Patrick Hofman

Il s'avère que non seulement les n-uplets peuvent être déconstruits, mais tout type ayant la méthode Deconstruct statique (ou extension) avec signature correspondante. Faire la déconstruction correctement pour IEnumerable n’est pas trivial (voir la bibliothèque suggérée par David Arno dans les commentaires), voyons donc comment cela fonctionne avec un simple IList (la mise en œuvre n’est pas pertinente, celle-ci est par exemple et peut bien sûr être meilleure/différente):

public static class Extensions {
    public static void Deconstruct<T>(this IList<T> list, out T first, out IList<T> rest) {

        first = list.Count > 0 ? list[0] : default(T); // or throw
        rest = list.Skip(1).ToList();
    }

    public static void Deconstruct<T>(this IList<T> list, out T first, out T second, out IList<T> rest) {
        first = list.Count > 0 ? list[0] : default(T); // or throw
        second = list.Count > 1 ? list[1] : default(T); // or throw
        rest = list.Skip(2).ToList();
    }
}

Ensuite (après avoir ajouté une instruction using pertinente si nécessaire), vous pouvez utiliser exactement la syntaxe souhaitée:

var list = new [] {1,2,3,4};
var (a,rest) = list;
var (b,c,rest2) = list;

Ou vous pouvez enchaîner la déconstruction comme ceci (parce que la dernière valeur retournée peut elle-même être déconstruite):

 var (a, (b, (c, rest))) = list;

Avec la dernière version, vous pouvez décomposer un nombre quelconque d'éléments à l'aide de la méthode unique Deconstruct (celle qui renvoie le premier élément et le reste).

Pour une utilisation réelle de IEnumerables, je vous conseillerais de ne pas réimplémenter la roue et d'utiliser la bibliothèque de David Arno mentionnée ci-dessus.

11
Evk

Ce que vous décrivez est généralement connu dans les langages fonctionnels comme "inconvénients", qui prend souvent la forme suivante:

let head :: tail = someCollection

J'ai proposé que cela soit ajouté à C # , mais il n'a pas reçu de commentaires très favorables. J'ai donc écrit le mien, que vous pouvez utiliser via le paquetage nuget Succinc <T> .

Il utilise la déconstruction pour scinder la tête et la queue de tout IEnumerable<T>. Les déconstructions peuvent être imbriquées, vous pouvez donc l'utiliser pour extraire plusieurs éléments en une fois:

var (a, (b, rest)) = someArray;

Cela pourrait potentiellement fournir la fonctionnalité que vous recherchez. 

10
David Arno

Vraiment rapide: n °

C # ne prend pas encore en charge la déstructuration des tableaux.

Actuellement, je ne trouve aucune information à ce sujet sur la feuille de route non plus. Il semble y avoir beaucoup d’attente avant que nous obtenions ce sucre syntaxique par défaut.

Comme @Nekeniehl l'a ajouté dans les commentaires, il peut être appliqué: Gist.github.com/waf/280152ab42aa92a85b79d6dbc812e68a

4
NikxDa

Dans C # vous devrez écrire le vôtre, comme celui que j'utilise:

public static class ArrayExtensions
    {
        public static void Deconstruct<T>(this T[] array, out T first, out T[] rest)
        {
            first = array.Length > 0 ? array[0] : default(T);
            rest = array.Skip(1).ToArray();
        }

        public static void Deconstruct<T>(this T[] array, out T first, out T second, out T[] rest)
            => (first, (second, rest)) = array;

        public static void Deconstruct<T>(this T[] array, out T first, out T second, out T third, out T[] rest)
            => (first, second, (third, rest)) = array;

        public static void Deconstruct<T>(this T[] array, out T first, out T second, out T third, out T fourth, out T[] rest)
            => (first, second, third, (fourth, rest)) = array;

        public static void Deconstruct<T>(this T[] array, out T first, out T second, out T third, out T fourth, out T fifth, out T[] rest)
            => (first, second, third, fourth, (fifth, rest)) = array;

// .. etc.
    }

Alors faites simplement:

var (first, second,_ , rest) = new[] { 1, 2, 3, 4 }
2
brakeroo

Il n'y a pas de syntaxe spéciale pour cela dans le langage.

Vous pouvez utiliser la syntaxe Tuple, cependant, pour arriver à cette 

class Program
{
    static void Main(string[] args)
    {
        int[] ints = new[] { 1, 2, 3 };

        var (first, second, rest) = ints.Destruct2();
    }
}

public static class Extensions
{
    public static (T first, T[] rest) Desctruct1<T>(this T[] items)
    {
        return (items[0], items.Skip(1).ToArray());
    }

    public static (T first, T second, T[] rest) Destruct2<T>(this T[] items)
    {
        return (items[0], items[1], items.Skip(2).ToArray());
    }
}

(qui devrait être étendu avec la gestion des erreurs pour les scénarios d'erreur évidents avant d'être utilisé dans le code de production).

0
Rune