Does C# 7 have array/enumerable destructuring?
Solution 1:
It turns out not only tuples can be deconstructed but any type which has Deconstruct
static (or extension) method with matching signature. Doing deconstruction correctly for IEnumerable
is not trivial (see library suggested by David Arno in this answer), so let's see how it works with simple IList
instead (implementation is irrelevant, this one is for example and of course can be better/different):
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();
}
}
Then (after adding relevant using statements if necessary) you can use exactly the syntax you want:
var list = new [] {1,2,3,4};
var (a,rest) = list;
var (b,c,rest2) = list;
Or you can chain deconstruction like this (because last returned value can itself be deconstructed):
var (a, (b, (c, rest))) = list;
With last version, you can deconstruct to any number of items using single Deconstruct
method (that one which returns first item and the rest).
For real usage for IEnumerables, I'd suggest to not reimplement the wheel and use David Arno's library mentioned in this answer.
Solution 2:
What you are describing is generally known in functional languages as "cons", which often takes the form:
let head :: tail = someCollection
I did propose this be added to C#, but it didn't receive very favourable feedback. So I wrote my own, which you can use via the Succinc<T> nuget package.
It uses deconstruction to achieve that splitting of the head and tail of any IEnumerable<T>
. Deconstructs can be nested, so you can use it to extract multiple elements in one go:
var (a, (b, rest)) = someArray;
This potentially could provide the functionality you are after.
Solution 3:
If you want a solution that is fully integrated with the C# language features, use Evk's answer, which hides some of the implementation detail. If you don't care about that, you can use either of the answers.
To my knowledge there is not. However, it is not very hard to make something similar.
What about an extension method like this:
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);
}
}
And you can use it like so:
int[] items = { 1, 2 };
items.Deconstruct(out int t0);
The drawback is that you need an extension method per number of items to return. So if you have more than a few variables to return, this method might not be very useful.
Note that I left out checking the length, and related stuff, but you understand what needs to be done I guess.
Solution 4:
To extend the solutions hinted by other contributors, I provide an answer that uses IEnumerable. It might not be optimized but it works quite well.
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;
}
Then just use these deconstructors like this:
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
Solution 5:
Really quick: No.
C# does not support destructuring for Arrays yet.
Currently, I cannot find any information of this on the roadmap, either. Seems like there will be a lot of waiting involved until we get this syntactic sugar by default.
As @Nekeniehl added in the comments, it can be implemented though: gist.github.com/waf/280152ab42aa92a85b79d6dbc812e68a