Имеет ли С# 7 массив/перечислимое деструктурирование?

В Javascript ES6 вы можете разрушить массивы следующим образом:

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

где a - первый элемент в массиве, b - второй, а rest - массив с остальными элементами.

В С# 7 я знаю, что вы можете разрушать кортежи во время назначения, но не могли найти ничего, связанного с деструктурированием массивов/перечислений, таких как:

var (a,b) = someTuple;

У меня есть IEnumerable, где мне нужны первые и второй элементы в качестве переменных, и мне нужны остальные элементы как другой IEnumerable. У меня есть решение, но чувствую, что деструктурирование будет выглядеть более чистым.

Ответ 1

Если вам нужно решение, полностью интегрированное с функциями языка С#, используйте Evk answer, в котором скрыты некоторые детали реализации. Если вас это не волнует, вы можете использовать любой из ответов.


Насколько мне известно, нет. Однако сделать что-то подобное не очень сложно.

Как насчет метода расширения, подобного этому:

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

    public static void Deconstruct<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);
    }
}

И вы можете использовать его так:

int[] items = { 1, 2 };

items.Deconstruct(out int t0);

Недостатком является то, что для возврата требуется метод расширения для каждого количества элементов. Так что если вам нужно вернуть несколько переменных, этот метод может оказаться не очень полезным.

Обратите внимание, что я не учел проверять длину и другие вещи, но вы понимаете, что нужно сделать, я думаю.

Ответ 2

Оказывается, что не только фрагменты могут быть деконструированы, но и любой тип, который имеет Deconstruct статический (или расширительный) метод с совпадающей сигнатурой. Выполнение деконструкции правильно для IEnumerable не является тривиальным (см. Библиотеку, предложенную Дэвидом Арно в комментариях), поэтому давайте посмотрим, как она работает с простым IList вместо этого (реализация не имеет значения, это, например, и, конечно, может быть лучше/отличается):

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();
    }
}

Затем (после добавления соответствующего оператора использования, если необходимо) вы можете использовать именно тот синтаксис, который вы хотите:

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

Или вы можете цеплять деконструкцию, как это (потому что последнее возвращаемое значение само может быть деконструировано):

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

С последней версией вы можете деконструировать любое количество элементов, используя единственный метод Deconstruct (тот, который возвращает первый элемент и остальное).

Для реального использования для IEnumerables я бы предложил не переопределять колесо и использовать библиотеку David Arno, упомянутую выше.

Ответ 3

То, что вы описываете, обычно известно на функциональных языках как "минусы", которые часто принимают форму:

let head :: tail = someCollection

Я предложил добавить это в С#, но оно не получило очень положительных отзывов. Поэтому я написал свой собственный, который вы можете использовать через Succinc & lt; T & gt; пакет Nuget.

Он использует деконструкцию для достижения такого разделения головы и хвоста любого IEnumerable<T>. Деконструкции могут быть вложенными, поэтому вы можете использовать их для извлечения нескольких элементов за один раз:

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

Это потенциально может обеспечить функциональность, которую вы ищете.

Ответ 4

Действительно быстро: Нет

С# еще не поддерживает деструктурирование для массивов.

В настоящее время я не могу найти информацию об этом в дорожной карте. Похоже, будет много ожиданий, пока мы не получим этот синтаксический сахар по умолчанию.

Как добавил @Nekeniehl в комментариях, он может быть реализован: gist.github.com/waf/280152ab42aa92a85b79d6dbc812e68a

Ответ 5

Чтобы расширить возможности, предложенные другими участниками, я предоставляю ответ, использующий IEnumerable. Возможно, он не оптимизирован, но работает довольно хорошо.

public static class IEnumerableExt
{
    public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out IEnumerable<T> rest)
    {
        first = seq.FirstOrDefault();
        rest = seq.Skip(1);
    }

    public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out IEnumerable<T> rest)
        => (first, (second, rest)) = seq;

    public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out T third, out IEnumerable<T> rest)
        => (first, second, (third, rest)) = seq;

    public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out T third, out T fourth, out IEnumerable<T> rest)
        => (first, second, third, (fourth, rest)) = seq;

    public static void Deconstruct<T>(this IEnumerable<T> seq, out T first, out T second, out T third, out T fourth, out T fifth, out IEnumerable<T> rest)
        => (first, second, third, fourth, (fifth, rest)) = seq;
}

Тогда просто используйте эти деконструкторы, как это:

var list = new[] { 1, 2, 3, 4 };
var (a, b, rest1) = list;
var (c, d, e, f, rest2) = rest1;
Console.WriteLine($"{a} {b} {c} {d} {e} {f} {rest2.Any()}");
// Output: 1 2 3 4 0 0 False

Ответ 6

В С# вам нужно написать свой собственный, как этот, который я использую:

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.
    }

Тогда просто сделай:

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

Ответ 7

В нем нет специального синтаксиса.

Однако вы можете использовать синтаксис кортежа, чтобы достичь этого

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());
    }
}

(который должен быть расширен с обработкой ошибок для очевидных сценариев ошибок перед использованием в производственном коде).