【问题标题】:Declaring and using a array of anonymous objects in C#在 C# 中声明和使用匿名对象数组
【发布时间】:2013-01-02 00:45:15
【问题描述】:

我们有多少次声明一个简单的类或结构来保存一些属性,但在方法返回时只使用一次。我想太多次了,谢天谢地,我们总是有可以在运行时声明的匿名对象。

考虑到这一点,我想知道如何声明一组此类匿名对象。

例子:

var Employee = new { ID = 5, Name= "Prashant" };

这创建了一个具有两个属性的匿名对象,一个是整数,另一个是字符串。

这里很好,但是我应该如何声明这种对象的数组,以及您建议如何使用 forforeach 循环遍历它。

foreach 循环确实是我认为的问题,因为 foreach 循环需要声明的类型。还是有远方的,如果有我当然也知道。

【问题讨论】:

  • @IchabodClay 你可能是对的,我没有查过。老实说,我相信编译器会将这些“匿名”对象声明为类来保存我们放入其中的任何内容。
  • @IchabodClay 它们是-在下面创建了适当的类。 (只是没有办法以良好类型的方式访问主格。现在,dynamic 对此类类型的数组进行强制..)

标签: c# .net anonymous-types


【解决方案1】:

我们有多少次声明一个简单的类或结构来保存一些属性,但在方法返回时只使用一次。我想太多次了,谢天谢地,我们总是有可以在运行时声明的匿名对象。

您的第一句话和第二句话表明您的目的是相互矛盾的。匿名类型以及匿名类型数组不能轻易地从方法中返回,因为无法声明返回类型。尽量只对临时局部变量使用匿名类型。

考虑到这一点,我想知道如何声明一组此类匿名对象。

像这样:

var array = new[] 
  { 
    new { X = 123, Y = 456 }, 
    new { X = 345, Y = 567 } 
  };

你会建议如何使用 for 和 foreach 循环来迭代它。

foreach(var item in array) 
...

for (int i = 0; i < array.Length; i += 1)
{
    var item = array[i];
    ...
}

【讨论】:

  • 嘿。正如他们所说,相信消息来源。 Eric,并不是说这是正确的“论坛”,但我一直想知道:匿名类型的“只读”性质从何而来?性能、解析复杂度还是其他?
  • @JerKimball:这是一个很好的问题。如果只有一个网站可以问这样的问题......
  • 很公平。以为我会偷偷溜过去。自从 Skeet 回答了我上一个关于清晰度的问题后,我就有点害羞了。 ;)
  • @JerKimball:简短的回答是:将查询的结果设为可变有什么引人注目的好处?稍长的答案与查找表键有关。见blogs.msdn.com/b/ericlippert/archive/2007/04/13/…blogs.msdn.com/b/sreekarc/archive/2007/04/03/…
  • 从按需类型的原始意图来看,这是完全合理的,尽管通常情况下,使用正在迅速扩展到原始意图之外。无论如何,这只是无聊的好奇心。我使用代码的时间越长,我就越倾向于不变性。谢谢!
【解决方案2】:

[编辑 - 更新以显示人口、基本枚举等]

正如@Eve 所说,LINQ 是您的朋友;作为一般的经验法则,不要尝试传递匿名类型 - 如果你很聪明,你可以 - 但是在上下文/范围之外处理它们是一个巨大的痛苦他们被宣布了。

顺便说一句,我决定看看以什么方式可以“声明一个匿名类型的数组”作为一个有趣的思想实验,并想出了这些:

(注意:“转储”是由于这是用 LINQPad 编写的)

// Our anonymous type sequence
var anonymousEnumerable = Enumerable
        .Range(0, 10)
        .Select(i => new { ID = i, Text = i.ToString() });
var enumerableCount = anonymousEnumerable.Count();
var anonymousType = anonymousEnumerable.First().GetType();

// Option #1 - declare it as dynamic, i.e., anything goes
dynamic[] asDynamicArray = new dynamic[enumerableCount];
foreach(var tuple in anonymousEnumerable.Select((item, i) => Tuple.Create(i, item)))
{
    asDynamicArray[tuple.Item1] = tuple.Item2;
}

// Let's go the IEnumerable route
foreach (var asDynamic in asDynamicArray)
{
    Console.WriteLine("ID:{0} Text:{1}", asDynamic.ID, asDynamic.Text);
}

// Lowest common denominator: *everything* is an object
object[] asObjectArray = new object[enumerableCount];
foreach(var tuple in anonymousEnumerable.Select((item, i) => Tuple.Create(i, item)))
{
    asObjectArray[tuple.Item1] = tuple.Item2;
}

// Let's iterate with a for loop - BUT, it's now "untyped", so things get nasty
var idGetterMethod = anonymousType.GetMethod("get_ID");
var textGetterMethod = anonymousType.GetMethod("get_Text");
for(int i=0;i < asObjectArray.Length; i++)
{
    var asObject = asObjectArray[i];
    var id = (int)idGetterMethod.Invoke(asObject, null);
    var text = (string)textGetterMethod.Invoke(asObject, null);
    Console.WriteLine("ID:{0} Text:{1}", id, text);
}

// This is cheating :)
var letTheCompilerDecide = anonymousEnumerable.ToArray();
foreach (var item in letTheCompilerDecide)
{
    Console.WriteLine("ID:{0} Text:{1}", item.ID, item.Text);
}

// Use reflection to "make" an array of the anonymous type
var anonymousArrayType = anonymousType.MakeArrayType();
var reflectIt = Activator.CreateInstance(
          anonymousArrayType, 
          enumerableCount) as Array;    
Array.Copy(anonymousEnumerable.ToArray(), reflectIt, enumerableCount);  

// We're kind of in the same boat as the object array here, since we
// don't really know what the underlying item type is
for(int i=0;i < reflectIt.Length; i++)
{
    var asObject = reflectIt.GetValue(i);
    var id = (int)idGetterMethod.Invoke(asObject, null);
    var text = (string)textGetterMethod.Invoke(asObject, null);
    Console.WriteLine("ID:{0} Text:{1}", id, text);
}

【讨论】:

  • 非常有趣的方法,但是当使用您在 reflectIt var 中使用的相同方法时,每个数组索引中的所有值都为空。全部复制并粘贴(删除了转储方法)并在调试模式下使用断点查看每个变量的值。
  • 哦,因为我没有在那个例子中填充数组;这只是创建一个空数组来存储该匿名类型的项目。很没用。 :)
  • 您关于如何声明数组的示例非常好,确定您不想像其他答案一样包含遍历它们吗?那是我唯一需要将您的答案标记为正确的事情,此时这样做不是很好的业力,因为其他人已将我提出的所有问题都包含在了问题中。你只是错过了。
  • @FábioAntunes 哈哈——当然,一会儿。
  • 相当不错。声明匿名对象数组的几种方法以及如何遍历它们的示例。对别人来说应该是一个很好的参考,就像对我一样。谢谢。
【解决方案3】:

您可以在foreach 中使用var

var Employee1 = new { ID = 5, Name = "Prashant" };
var Employee2 = new { ID = 1, Name = "Tim" };
var employees = new[] { Employee1, Employee2 };

foreach (var employee in employees)
{
    Console.WriteLine("ID:{0}, Name:{1}", employee.ID, employee.Name);
}

for (int i = 0; i < employees.Length; i++)
{
    Console.WriteLine("ID:{0}, Name:{1}", employees[i].ID, employees[i].Name);
}

Demo

【讨论】:

  • 关于如何使用基于集合的类来存储匿名对象的任何想法,可能是 List 而不是您作为示例提供的数组。好奇您是否对此有任何想法,我认为这并不容易,而且可能是不可能的,因为 List 及其基于集合的通用派生类都希望声明一个类型。但也许你对此有任何想法。
  • There are already Stackoverflow 上有很多关于此的问题。 Here's 另一个快速搜索命中,它描述了如何从方法返回匿名类型。但是您应该仔细阅读有关风险的部分。
  • 是的,声明一个 List 是 T 对象,也可能是动态的。我知道它们中的任何一个都会起作用,但我想也许有更好的方法。无论如何,谢谢。
【解决方案4】:

您可以使用 LINQ 来实现您所需要的。这是一个例子。

int[] ids = {10, 15, 99};
string[] names = {"John", "Phil", "Jack"};
var array = ids.Zip(names, (id, name) => new {ID = id, Name = name}).
                ToArray();

或者,如果您只需要数组但没有数据,则可以使用此解决方法。但是,结果将是一个Array,没有关于元素类型的信息。

var sample = new {ID = default(int), Name = default(string)};
var arrayLength = 10;
var array = Array.CreateInstance(sample.GetType(), arrayLength);

关于迭代,你可以在你的for/foreach循环中使用var来避免声明类型的问题。

foreach (var item in array)
{
    //Do something
}

for (var i = 0; i < array.Length; i++)
{
    var item = array[i];
    //Do something
}

编辑:基于示例创建具有任意数量元素的空数组的另一种解决方法。

public static T[] GetArray<T>(T sample, int amount)
{
    return new T[amount];
}

【讨论】:

  • 对,忘了 foreach 循环也可以接受“var”作为对象类型。现在关于声明数组,是否有一种直接的方法,无需在对象中声明每种属性类型的数组并对其进行排序?编辑:这种解决方法似乎正是我所需要的。只是需要测试一下。
  • @FábioAntunes 我已经用一些解决方法编辑了我的帖子。可能有更多方法可以做到这一点,但这是我想到的第一个。
  • foreach 中使用解决方法中的代码将导致item 的类型为object,而不是所需的匿名类型。
  • @mikez True,正如我在帖子中提到的。目前正在尝试找到一个简单的解决方案来获取类型化的空数组。
猜你喜欢
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
  • 2015-09-11
  • 1970-01-01
相关资源
最近更新 更多