如何在不知道 C# 中的底层类型的情况下遍历数组

How to iterate thru an array without knowing the underlying type in C#

假设我有一个对象集合,其中一些是数组。

我想连接所有的值来打印它们,但我不知道数组中元素的类型。

foreach(var item in myList)
{
    var val = item.Property;
    if (val.GetType().IsArray)
    {
        var array = val as IEnumerable<object>;
        val = string.Join(",", array);
    }
    DoSomething(val);
}

如果 val 包含 string[],此代码片段将适用,也适用于 myClass[]

但如果它包含 int[]double[],则数组将为空,这意味着动态转换失败。

如果 int 或 double 是从 System.TypeValue 继承的,而 System.TypeValue 是从 object 继承的,为什么这个代码片段不起作用?

我怎样才能做到这一点?

编辑:将代码片段更改为更明确,并避免了因为我错误地简化了代码而出现的错误变量使用。

在 C# 中不允许这样做,更多详细信息 here。 但是你可以转换为 non-generic IEnumerable,然后在推送到 string.Join():

之前将所有内容转换为对象
foreach(var val in myList)
{
    if (val.GetType().IsArray)
    {
        var array = (IEnumerable)val;
        // It's not allowed to set val variable, but I assume it's just an example
        val = string.Join(",", array.Cast<object>());
    }
}

首先,尝试在 foreach 循环中分配给 val 是行不通的。您无法更改正在迭代的集合。

所以你需要建立一个新的集合。像这样的东西,看看迭代器中的 yield return 语句如何让你建立一个新的 IEnumerable 叶对象,并在任何级别上对对象和值类型都起作用。

    class Program
    {
        static void Main(string[] args)
        {
            var myWackyList = new object[] {
                new[]{1d, 2d},
                3d,
                new[]{4d, 5d},
                new []
                {
                    new[]
                    {
                        6d
                    }
                },
                "7",
                new[]{ "8", "9"}
            };

            Console.WriteLine( string.Join(", ", Flatten( myWackyList )));
        }

        static IEnumerable<object> Flatten(IEnumerable enumerable)
        {
            foreach (var val in enumerable)
            {
                if ( val.GetType().IsArray )
                    foreach ( var flattenedVal in Flatten( val as IEnumerable ) )
                        yield return flattenedVal;
                else
                    yield return val;
            }
        }
    }

如果你只是担心一般的数组,或者更好的是IEnumerable(根据你的演员表)那么我建议离开检查的反射部分并简单地尝试转换为 IEnumerable.

foreach (var val in myList)
{
    if (val is IEnumerable enumeration)
    {
        string.Join(",", array); //You can't use val (as per your example because it's the element of the loop)
    }
}

老实说,我不知道您打算对数组做什么,因为您将它作为对象添加到字符串中,这将只是 array, array, array... 等等。我打算 post 一个更好的解决方案,但是我不确定它是否是您想要的,因为您在上面所做的事情没有多大意义。

var notSureWhyStringBuilder = new StringBuilder();
foreach (var val in myList)
{
    if (val is IEnumerable enumeration)
    {
        notSureWhyStringBuilder.Append($",{enumeration.ToString()}");
    }
}
Console.WriteLine(notSureWhyStringBuilder.ToString());

现在至少我觉得你更朝着你想要的方向前进,但这对我来说并不合适,因为我不知道你会从中获得什么它。

我将要 post 另一个示例,该示例将迭代并将内部枚举构建到字符串中。我不知道或假设这就是您想要的,但在我提供的 3 个示例中,希望您可以拿走并重新设计它以获得您需要的东西并可能从中学习。

var notSureWhyStringBuilder = new StringBuilder();
foreach (var val in myList)
{
    if (val is IEnumerable enumeration)
    {
        foreach (var innerEnumeration in enumeration)
        {
            notSureWhyStringBuilder.Append($",{innerEnumeration.ToString()}");
        }
    }
}
Console.WriteLine(notSureWhyStringBuilder.ToString());

这是我整理的一个小型控制台应用程序,供您随意使用。按原样复制并粘贴它,它应该可以工作。

控制台应用程序

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Question_Answer_Console_App
{
    class Program
    {
        private const string ShortTab = "  ";
        private static readonly List<object> ListOfObjects = new List<object>()
        {
            4,
            "Michael",
            new object(),
            new Program(),
            69.4,
            new List<string>() {"Mathew", "Mark", "Luke", "John" },
            new int[] { 1, 3, 5, 7, 9 }
        };

        static void Main(string[] args)
        {
            var itemsText = new StringBuilder();
            var arrayCounter = 0;

            foreach (var item in ListOfObjects)
            {
                if (item is IEnumerable enumeration)
                {
                    itemsText.AppendLine($"Array: {++arrayCounter}");
                    if (item is string text)
                    {
                        itemsText.AppendLine($"{ShortTab}{text}");
                    }
                    else
                    {
                        foreach (var innerItem in enumeration) itemsText.AppendLine($"{ShortTab}{innerItem.ToString()}");
                    }
                }
            }
            Console.WriteLine(itemsText.ToString());
            Console.Read();
        }
    }
}

输出

Array: 1
  Michael
Array: 2
  Mathew
  Mark
  Luke
  John
Array: 3
  1
  3
  5
  7
  9

您不能将值类型数组转换为 IEnumerable,因为只有在不需要表示转换时才适用方差。因此它不适用于值类型。在这种特殊情况下,这种转换意味着装箱。

我会这样做是为了防止不必要的装箱,例如在使用 Cast 的情况下。

public static string Stringify(this object o, string delimiter)
{
    if (!(o is IEnumerable enumerable))
        return o.ToString();

    var sb = new StringBuilder();

    foreach (var i in enumerable)
    {
        if (sb.Length > 0)
            sb.Append(delimiter);

        sb.Append(i.ToString());
    }

    return sb.ToString();
}