web-dev-qa-db-ja.com

C#7には配列/列挙可能な構造化がありますか?

Javascript ES6では、次のように配列を非構造化できます。

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

aは配列の最初の要素、bは2番目、restは残りの要素を含む配列です。

C#7では、割り当て中にタプルを分解できることを知っていますが、次のような配列/列挙可能データの破壊に関連するものは見つかりませんでした。

var (a,b) = someTuple;

最初の要素と2番目の要素を変数として必要とするIEnumerableがあり、残りの要素は別のIEnumerableとして必要です。私は解決策を持っていますが、破壊はよりきれいに見えると感じています。

31
kmc059000

C#言語機能と完全に統合されたソリューションが必要な場合は、 Evkの答え を使用します。これにより、実装の詳細の一部が隠されます。気にしない場合は、どちらの答えでも使用できます。


私の知る限りではありません。ただし、似たようなものを作るのはそれほど難しくありません。

このような拡張メソッドはどうですか:

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

欠点は、返されるアイテムの数ごとに拡張メソッドが必要なことです。したがって、返す変数が数個を超える場合、このメソッドはあまり役に立ちません。

私は長さや関連するもののチェックを省略したことに注意してください。

9
Patrick Hofman

タプルを分解できるだけでなく、シグネチャが一致する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のライブラリを使用することをお勧めします。

21
Evk

あなたが記述していることは、関数型言語では一般に「cons」として知られ、しばしば次の形式を取ります。

let head :: tail = someCollection

これをC#に追加することを提案しました ですが、あまり好意的なフィードバックはありませんでした。そこで、私は自分で作成しました Succinc <T> nuget package で使用できます。

分解を使用して、IEnumerable<T>の頭と尾の分割を実現します。デコンストラクトはネストできるため、これを使用して複数の要素を一度に抽出できます。

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

これにより、必要な機能が提供される可能性があります。

16
David Arno

他の貢献者からヒントを得たソリューションを拡張するために、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
4
Frederic

本当に速い:いいえ

C#は、配列の非構造化をまだサポートしていません。

現在、ロードマップでもこの情報を見つけることができません。デフォルトでこの構文糖を取得するまで、多くの待機が必要になるようです。

@Nekeniehlがコメントに追加したように、それは実装できます: Gist.github.com/waf/280152ab42aa92a85b79d6dbc812e68a

4
NikxDa

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 }
3
brakeroo

言語には特別な構文はありません。

ただし、Tuple構文を活用して、これに到達することができます。

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

(本番コードで使用する前に、明らかなエラーシナリオのエラー処理で拡張する必要があります)。

0
Rune