如何在不知道 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();
}
假设我有一个对象集合,其中一些是数组。
我想连接所有的值来打印它们,但我不知道数组中元素的类型。
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();
}