简体   繁体   English

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

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

Let's say I have a collection of objects, some of which are arrays. 假设我有一个对象集合,其中一些是数组。

I would like to concat all the values to print them, but I don't know the type of elements in the arrays. 我想合并所有值以打印它们,但我不知道数组中元素的类型。

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

If val contains a string[] , this code snippet will work, also for a myClass[] . 如果val包含string[] ,则此代码段也适用于myClass[]

But if it contains a int[] or double[] , then array will be null, meaning the dynamic cast failed. 但是,如果它包含int[]double[] ,则array将为null,这意味着动态转换失败。

If int or double are inherited from System.TypeValue, which inherits from object, why don't this code snippet work? 如果int或double是从System.TypeValue继承的,而System.TypeValue是从object继承的,那么为什么此代码段不起作用?

And how could I achieve that? 我该如何实现呢?

Edit: changed the code snippet to be more explicit and avoid the wrong variable usage that did showup because I wrongly simplified my code. 编辑:将代码段更改为更明确的代码,并避免出现错误的变量用法,因为我错误地简化了代码。

It's not allowed to do so in C#, more details here . 在C#中不允许这样做,更多详细信息在这里 But you can cast to non-generic IEnumerable , and then cast everything to object before pushing to string.Join() : 但是您可以将其string.Join()转换为非泛型 IEnumerable ,然后将所有内容string.Join()为对象,然后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>());
    }
}

First off, trying to assign to val inside the foreach loop will not work. 首先,尝试在foreach循环内分配给val无效。 You can't change a collection you are iterating over. 您不能更改要迭代的集合。

So you need to build up a new collection. 因此,您需要建立一个新集合。 Something like this works, see how the yield return statement in the iterator lets you build up a new IEnumerable of the leaf objects and works for both objects and value types at any level. 像这样的事情,看看迭代器中的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;
            }
        }
    }

If you're just worried about arrays in general, or better yet IEnumerable (as per your cast) then I suggest leaving the reflective portion of checks out and simply attempting to convert to 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)
    }
}

I honestly don't know what you're intending to do with the array, as your adding it to a string as an object, which will just be array, array, array... and so on pretty much. 老实说,我不知道您打算对数组做些什么,因为您将其作为对象添加到字符串中,这几乎就是array, array, array...等等。 I'm going to post a better solution however I'm not sure if it's what you want or not because what you're doing above doesn't make a lot of sense. 我将发布一个更好的解决方案,但是我不确定这是否是您想要的,因为您在上面所做的事情没有多大意义。

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

Now with this at least I feel like you're more in the direction that you want to be in but that doesn't sit well with me because I don't know what you're going to gain out of it. 现在至少有了这种感觉,我觉得你正朝着自己想要的方向发展,但这与我的关系不佳,因为我不知道你将从中得到什么。

I'm going to post one more example, one that will iterate and build the inner enumeration into the string for you. 我将再发布一个示例,该示例将为您迭代并将内部枚举构建到字符串中。 I don't know or assume that's what you want but between the 3 examples I'm providing hopefully you can take away and re-engineer it to get what you need and possibly learn from it. 我不知道或不知道这是您想要的,但是在我提供的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());

Here's a small console app that I put together for you to piddle with. 这是一个小型控制台应用程序,我整理起来供您使用。 Copy and paste it as is and it should work. 复制并粘贴它,它应该可以工作。

Console App 控制台应用

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

Outputs 输出

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

You cannot cast a value-type array to an IEnumerable because variance only applies if there's no representational conversion needed. 您不能将值类型数组转换为IEnumerable,因为方差仅在不需要表示性转换时才适用。 Therefore it doesn't apply for value types. 因此,它不适用于值类型。 In this particular case this conversion would mean boxing. 在这种特殊情况下,这种转换意味着装箱。

I would do it this way to prevent unnecessary boxing, like in the case with Cast, for example. 我将以这种方式执行此操作,以防止不必要的装箱,例如在使用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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 C# 中,如何在编译时不知道类型的情况下访问用户定义类型的 static 属性? - In C#, how can you access a static property of a user-defined type without knowing the type at compiletime? 在不知道数组大小的情况下从C到C#进行P /调用 - P/Invoke from C to C# without knowing size of array 将String转换为Enum而不知道它在C#中的类型 - Convert String in to Enum Without knowing it's type in C# C#在不知道特定类型的情况下访问泛型方法 - C# Accessing generic Method without knowing specific type C#在不知道对象类型的情况下获取对象的名称 - C# Get the name of an object without knowing its Type C#将作为通用对象返回的数组转换为不同的基础类型 - C# converting an array returned as a generic object to a different underlying type 如何在不知道对象类型的情况下在C#中的DataBound DataGrid中移动行 - How To Move Row In DataBound DataGrid In C# without Knowing The Object Type 在C#中声明不知道其大小的2D数组 - Declaring a 2D array without knowing its size in C# C#:如何在不知道数组大小的情况下使用string.Format格式化字符串? - C#: How to format a string using string.Format without knowing the size of an array? 如何在不知道键的类型和值的类型的情况下迭代KeyValuePair的枚举 - How to iterate through an enumeration of KeyValuePair without knowing the type of the key and the type of the value
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM