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

C# 如何在不知道C中的底层类型的情况下遍历数组#,c#,arrays,casting,ienumerable,C#,Arrays,Casting,Ienumerable,假设我有一个对象集合,其中一些是数组 我想将所有的值合并以打印它们,但我不知道数组中元素的类型 foreach(var item in myList) { var val = item.Property; if (val.GetType().IsArray) { var array = val as IEnumerable<object>; val = string.Join(",", array); } DoS

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

我想将所有的值合并以打印它们,但我不知道数组中元素的类型

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);
}
foreach(myList中的变量项)
{
var val=项目属性;
if(val.GetType().IsArray)
{
var数组=作为IEnumerable的val;
val=string.Join(“,”数组);
}
剂量测定法(val);
}
如果val包含
字符串[]
,则此代码段也适用于
myClass[]

但如果它包含一个
int[]
double[]
,那么数组将为null,这意味着动态强制转换失败

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

我怎样才能做到呢


编辑:将代码段更改为更显式,并避免出现错误的变量用法,因为我错误地简化了代码。

在C#中不允许这样做,更多详细信息。 但您可以强制转换为非泛型
IEnumerable
,然后在按下
string.Join()之前将所有内容强制转换为object:

foreach(myList中的var val)
{
if(val.GetType().IsArray)
{
变量数组=(IEnumerable)val;
//不允许设置val变量,但我假设这只是一个示例
val=string.Join(“,”,array.Cast());
}
}

首先,尝试在
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;
            }
        }
    }
类程序
{
静态void Main(字符串[]参数)
{
var myWackyList=新对象[]{
新[]{1d,2d},
3d,
新[]{4d,5d},
新[]
{
新[]
{
6d
}
},
"7",
新[]{“8”,“9”}
};
Console.WriteLine(string.Join(“,”,Flatten(myWackyList));
}
静态IEnumerable展平(IEnumerable enumerable)
{
foreach(可枚举中的var val)
{
if(val.GetType().IsArray)
foreach(变平中的变平val(val为IEnumerable))
收益率平坦化;
其他的
收益率;
}
}
}

如果您只是担心数组的一般性,或者更好的是
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…
等等。我将发布一个更好的解决方案,但是我不确定它是否是你想要的,因为你上面所做的没有多大意义

var notSureWhyStringBuilder = new StringBuilder();
foreach (var val in myList)
{
    if (val is IEnumerable enumeration)
    {
        notSureWhyStringBuilder.Append($",{enumeration.ToString()}");
    }
}
Console.WriteLine(notSureWhyStringBuilder.ToString());
现在,至少我觉得你更倾向于你想要的方向,但这对我来说并不合适,因为我不知道你会从中得到什么

我将再发布一个示例,它将为您迭代并将内部枚举构建到字符串中。我不知道或者假设这就是你想要的,但是在我提供的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();
        }
    }
}

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

我会这样做,以防止不必要的拳击,如在案件与铸造,例如

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

@Maess double和int是对象,但它们也是值类型(不是原语),因此不能涉及方差。是的,对不起,我简化了代码,没有看到我在foreach循环中使用了相同的
val
变量名。非常感谢,
IEnumerable
类没有出现,我注意到它不在同一个名称空间中,这使我走错了方向。这个很有魅力!谢谢你的详细回答。在这种情况下,我的目标是使用反射在控制台上打印对象的所有属性的值。我想处理值是一个值数组的情况,然后简单地用分隔符连接这些值。我希望代码尽可能通用,因为我在设计时不知道数组中会有什么类型的值,这取决于我处理的对象。@Sierramike没有问题。您可能希望使用递归函数,因为如果这些数组包含数组而不是字符串,该怎么办。为了速度和资源,我建议使用stringbuilder。反思很有趣:)坚持下去。
Array: 1
  Michael
Array: 2
  Mathew
  Mark
  Luke
  John
Array: 3
  1
  3
  5
  7
  9
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();
}