将数组转换为元组?

17
在C#中是否可能将数组转换为元组?类似这样的操作:
var ar = new int[2] {5, 7};
Tuple<int,int> t = Tuple.Create(ar);

2
如果您知道数组的大小,只需传递ar[0],ar[1]的值即可。 - michip96
6
只有在事先知道数组的大小时,这才有意义。然后使用 C#7 可以这样做:(int a, int b) = (ar[0], ar[1]);(这种方式的优点是可以为元组元素命名)。 - Matthew Watson
这里的主要问题是数组的大小(以及Tuple<>的大小)必须在编译时知道:在你编写的代码中,你明确地写了Tuple<int, int>。你可以轻松创建一个方法,给定一个包含两个(或更多)元素的数组,返回一个Tuple<T, T> - xanatos
6个回答

11

现在使用 C# 7.0,你可以创建扩展方法将数组解构为 ValueTuple,这使得

var (p1,p2,p3) = s.Split(':');

可能的。

public static class ArrayExt {
    public static void Deconstruct<T>(this T[] srcArray, out T a0) {
        if (srcArray == null || srcArray.Length < 1)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
    }

    public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1) {
        if (srcArray == null || srcArray.Length < 2)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
        a1 = srcArray[1];
    }

    public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2) {
        if (srcArray == null || srcArray.Length < 3)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
        a1 = srcArray[1];
        a2 = srcArray[2];
    }

    public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3) {
        if (srcArray == null || srcArray.Length < 4)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
        a1 = srcArray[1];
        a2 = srcArray[2];
        a3 = srcArray[3];
    }

    public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4) {
        if (srcArray == null || srcArray.Length < 5)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
        a1 = srcArray[1];
        a2 = srcArray[2];
        a3 = srcArray[3];
        a4 = srcArray[4];
    }

    public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5) {
        if (srcArray == null || srcArray.Length < 6)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
        a1 = srcArray[1];
        a2 = srcArray[2];
        a3 = srcArray[3];
        a4 = srcArray[4];
        a5 = srcArray[5];
    }

    public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5, out T a6) {
        if (srcArray == null || srcArray.Length < 7)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
        a1 = srcArray[1];
        a2 = srcArray[2];
        a3 = srcArray[3];
        a4 = srcArray[4];
        a5 = srcArray[5];
        a6 = srcArray[6];
    }

    public static void Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2, out T a3, out T a4, out T a5, out T a6, out T a7) {
        if (srcArray == null || srcArray.Length < 8)
            throw new ArgumentException(nameof(srcArray));

        a0 = srcArray[0];
        a1 = srcArray[1];
        a2 = srcArray[2];
        a3 = srcArray[3];
        a4 = srcArray[4];
        a5 = srcArray[5];
        a6 = srcArray[6];
        a7 = srcArray[7];
    }
}

Deconstruct在使用时是否会隐式调用?例如,由于LHS是(p1,p2,p3),它会检查类是否具有带有3个out参数的方法或扩展方法,还是需要调用类似于s.Split(':').Deconstruct();这样的语法? - Jeppe
或者它看起来必须被称为Deconstruct,具体地说:deconstruct#user-defined-types - Jeppe
1
@Jeppe 你必须明确定义这个方法,但是当你在解构上下文中使用你的用户定义类型(或其他具有扩展方法的类型)时,它会隐式调用:var arr = new[] { 1, 2, 3 }; var (a1,a2,a3) = arr; 会隐式调用 Deconstruct<T>(this T[] srcArray, out T a0, out T a1, out T a2) - NetMage

4
不,System.Tuple 有一个最大的大小限制,这是有充分理由的。它显然不适合你所要做的工作。为什么不返回数组而不是元组呢?你的方法可能需要一个包含数十个元素的元组,这是荒谬的,并且难以维护。
更好的方法是不要返回数组,而是返回接口,如 ICollection<T>IEnumerable<T>

完全没有可维护性 - 这正是我想说的。 - quetzalcoatl
8
我认为提问者可能在考虑编译时已知数组大小的情况。例如,一个比元组更早的API返回一个由四个字符串组成的数组。或者如果您使用已知捕获字段数量的正则表达式匹配,现在想将结果打包成元组。 - Ed Avis
问题不应该带有主观色彩,答案也是如此。我认为这个回答未能回答问题。 - Jeppe
嗯,你可能需要更新这里大概90%的答案。 - user9993

-1

那我们为什么不这样做呢?

public static Tuple<T, T> CreateTuple<T>(T[] array, int totalItem = 2)
{
    if (arr.length % totalItem != 0)
    throw new Exception("Error the length dose not consist with totalItem");

    return Tuple.Create(arr);
}

在这里,我们验证每个项目是否具有一个和两个。


有时候会出现非常适合这样做的情况,比如数组大小已知。因此,一般来说认为这是一个坏主意有点过于苛刻了。 - Frank Bakker
这可能会产生编译器错误: 错误 CS0029:无法隐式转换类型'System.Tuple<T[]>'到'System.Tuple<T,T>' - Joerg

-1
通过这样做,您可以获得一个元组列表,但我不认为这样做有什么理由:
      var ar = new int[2] { 5, 7 };

        List<Tuple<int>> result = ar
            .Select(x => Tuple.Create(x))
            .ToList();

或者如果你想要将它放在一个元组中:

   var ar = new int[2] { 5, 7 };
    Tuple<int[]> result = Tuple.Create(ar);

如果你知道大小,就这样做:
  Tuple<int, int> tuple = Tuple.Create(ar[0], ar[1]);

-1

你可以像这样使用子元组,比如:

var tupArray = new Tuple<dynamic,dynamic,dynamic,dynamic,dynamic,dynamic,dynamic,Tuple<dynamic,dynamic>>
(
    a[0],a[1],a[2],a[3],a[4],a[5],a[6],new Tuple<dynamic,dynamic>(a[7],a[8])
)

-3

你的想法不太好...在C#/.NET中,数组和元组不能“混合使用”。有些语言中,数组和类之间的距离很小(比如Javascript,你可以使用[...]运算符同时使用数组和对象)。但C#不是这样的语言。Tuple正是因为这个原因没有定义this[int n]索引器。

但既然你要代码,那我就给你代码。

主要问题在于数组的大小(以及Tuple<>的大小)必须在编译时知道:在你写的代码中,你明确地写了Tuple<int, int>。你可以轻松创建一个方法,给定一个包含两个(或更多)元素的数组,返回一个Tuple<T, T>,例如:

public static Tuple<T, T> CreateTuple2<T>(T[] array)
{
    if (array == null || array.Length < 2)
    {
        throw new ArgumentException(nameof(array));
    }

    return Tuple.Create(array[0], array[1]);
}

从这里你可以创建其他的CreateTuple3()CreateTuple4()...

1
这是糟糕的建议。 - user9993
@user9993,我认为这是一个“答案”,而不是“建议”。这个“问题”很糟糕(在C#/.NET中混合使用数组和元组是一个坏主意),但他要求一段代码(是否可能将数组转换为元组在C#中?类似于这样:),所以我给了他。 - xanatos
1
理论上说,Tuple<>的大小不需要知道:您可以编写一个单一的方法,通过反射动态构建Tuple<,,,,,,>,根据数组的大小调整其大小(假设它不超过元组的最大N参数),并将其作为“object”或“dynamic”返回,并对其用户的未来问题感到邪恶的笑声(哦,你得到了对象?所以只需转换它muahahaha)。或创建一个单一的TTuple CreateTuple<TTuple>(object[] vals),以类似的方式工作,并以类似的方式发出邪恶的笑声(只需调用CreateTuple<Tuple<int,string,int,...>..)或(...)。 - quetzalcoatl
@quetzalcoatl 是的... 而且所有这些方法可能都比想要将数组转换为元组更愚蠢 :-) - xanatos
7
然而我想要执行 var (p1,p2,p3) = str.Split(':'); - NetMage
1
现在我想在C#中实现Perl的unpack,并且能够像Perl一样使用元组赋值:var (s1,s2) = unpack("A4x2A4", test); - NetMage

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接