C#7有阵 列/可枚举的解构吗?

问题描述 投票:25回答:7

在Javascript ES6中,您可以像这样构造数组:

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

其中a是数组中的第一个元素,b是第二个,rest是一个包含其余元素的数组。

我知道在C#7中你可以在赋值时解构元组,但是找不到任何与解构数组/枚举相关的东西:

var (a,b) = someTuple;

我有一个IEnumerable,我需要第一个和第二个元素作为变量,我需要其余的元素作为另一个IEnumerable。我有一个解决方案,但觉得解构会看起来更干净。

c# destructuring c#-7.0
7个回答
8
投票

不,那里没有。但是,制作类似的东西并不是很难。

这样的扩展方法怎么样:

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

你可以像这样使用它:

int[] items = { 1, 2 };

items.Destructure(out int t0);

缺点是您需要每个项目的扩展方法才能返回。因此,如果要返回的变量不止一些,则此方法可能不太有用。

请注意,我遗漏了检查长度和相关内容,但你知道我猜需要做什么。


18
投票

事实证明,不仅元组可以被解构,而且任何具有匹配签名的Deconstruct静态(或扩展)方法的类型。正确地为IEnumerable做解构并不是一件容易的事(参见David Arno在评论中提到的库),所以让我们看看它是如何与简单的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();
    }
}

然后(如果需要,在添加相关的using语句之后),您可以使用您想要的语法:

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的库。


16
投票

您所描述的内容通常在函数式语言中称为“cons”,通常采用以下形式:

let head :: tail = someCollection

I did propose this be added to C#,但它没有得到非常有利的反馈。所以我写了自己的,你可以通过Succinc<T> nuget package使用。

它使用解构来实现任何IEnumerable<T>的头部和尾部的分裂。解构可以嵌套,因此您可以使用它来一次性提取多个元素:

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

这可能会提供您所需的功能。


4
投票

真的很快:没有。

C#还不支持Arrays的解构。

目前,我在路线图上也找不到任何相关信息。似乎在我们默认获得这种语法糖之前会有很多等待。

正如@Nekeniehl在评论中补充的那样,它可以实现:gist.github.com/waf/280152ab42aa92a85b79d6dbc812e68a


3
投票

在C#中你需要自己编写,就像我正在使用的那样:

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 }

2
投票

为了扩展其他贡献者所暗示的解决方案,我提供了一个使用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

0
投票

语言中没有特殊的语法。

但是,您可以利用元组语法来实现此目的

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

(在生产代码中使用之前,应该针对明显的错误情况扩展错误处理)。

© www.soinside.com 2019 - 2024. All rights reserved.