【问题标题】:Constructing generic lists of anonymous types and nested loops构造匿名类型和嵌套循环的通用列表
【发布时间】:2010-08-13 01:34:43
【问题描述】:

我希望通过在嵌套循环中遍历其他两个列表来构造一个匿名类型列表。

var myList = new List<???>();
foreach (object x in GetAllX())
{
    if (Process(x))
    {
        foreach (object y in GetAllY(x))
        {
            myList.Add(new {
                X = x,
                Y = y
            });
        }
    }
}

我知道我可以使用ToList() 构造一个匿名类型列表(请参阅this question),但在上述情况下我不知道如何使用它。

请注意,我无法更改 GetAllXGetAllY 方法。

【问题讨论】:

标签: c# generics list anonymous-types


【解决方案1】:

简单的答案是“你不应该”。

有一个 hacky 技巧可以让你做到这一点:

var myList = new[] { new { X = (object) null, Y = (object) null } }.ToList();
myList.Clear();
foreach (object x in GetAllX())
    // ...

但是按照预期的方式使用它确实更合理:

var myList = GetAllX().Where(x => Process(x))
    .SelectMany(x => GetAllY(x).Select(y => new { X = x, Y = y }))
    .ToList();

如果由于某种原因你真的不能使用这种纯函数式风格,或者你发现你必须在多个地方实例化这样的列表,你可能应该声明一个普通类而不是使用匿名类型。请记住,匿名类型无论如何都会编译成类,因此匿名类型没有性能优势,如果您不得不求助于本文顶部的 hacky 之类的技巧,那么即使是可读性/可维护性优势也是值得怀疑的。

有些人建议使用List&lt;dynamic&gt;,但我不建议这样做。它严重妨碍了可维护性,因为不再在编译时检查属性名称和类型(您可能会输入错误并得到运行时错误);它会降低运行时性能,因为每次访问都要经过动态调度程序;而且,一旦你把你的对象放到这个列表中,你基本上就被它们动态地困住了,因为你不能把它们转换回匿名类型。

【讨论】:

    【解决方案2】:

    这段代码难道不是实现您想要的结果的最简单方法吗?

    var myList = (from x in GetAllX()
                  where Process(x)
                  from y in GetAllY(x)
                  select new
                  {
                      X = x,
                      Y = y,
                  }).ToList();
    

    (Timwi - 我知道这是您解决方案的“linqified”版本,但我想我会发布它,因为我觉得这种风格很容易阅读和遵循。)

    【讨论】:

      【解决方案3】:

      这里有两个选择。首先,您可以创建一个具有 XY 属性的简单类,并让您的列表成为该类的对象列表,如下所示:

      class NewClass
      {
          public object X;
          public object Y;
      }
      
      var myList = new List<NewClass>();
      foreach (object x in GetAllX())
      {
          if (Process(x))
          {
              foreach (object y in GetAllY(x))
              {
                  myList.Add(new NewClass() {
                      X = x,
                      Y = y
                  });
              }
          }
      }
      

      或者,您可以在 C# 4.0 中使用它:

      var myList = new List<dynamic>();
      

      【讨论】:

      • 使用第一个选项可以获得更强大的类型检查。
      【解决方案4】:

      既然你把 X 和 Y 放在同一个列表中,它们必须有一个共同的基类/接口。什么?他们之间没有关系?那你为什么要把它们放在同一个列表中!这不是一个好主意。

      IEnumerable<BaseClass> AllXY = 
                  GetAllX().Cast<BaseClass>().Union(GetAllY().Cast<BaseClass>());
      foreach(var base in AllXY)
      {
          //do something to base, usually with polymorphism
      }
      

      【讨论】:

      • 为什么? X = 类别,Y = 该类别中的事物,或者 X = 目录,Y = 该目录中的文件。在结果列表中仍然保持 X 和 Y 分开是个好主意的情况下,您可能想要这样做的原因有很多。
      • X=direcotires, Y=files,把它们放在一个列表中是没有意义的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-01
      • 1970-01-01
      • 2020-07-07
      • 2010-12-25
      • 1970-01-01
      • 2018-01-06
      • 1970-01-01
      相关资源
      最近更新 更多