匿名类型集合?

8
我正在寻找创建由匿名类型构成的集合的最佳实践。
有几种方法-this其中一种方法和this线程上的大多数答案都假定整个匿名集合可以在一个语句中构造。
由于匿名类型通常用于替换要用于存储临时数据的类(如this SO answer中提出的),所以我想避免为匿名集合创建和使用类,正如this post所建议的那样。
在我的情况下,我正在迭代一个集合-对于集合中的每个项目,我想收集一个匿名类型中的相关对象(作为元组)。 我需要将这些匿名类型放入集合中,然后对它们进行排序以进一步处理。

我考虑过的其他方法:

  1. 使用object[]或ArrayList(失去匿名类型的好处)
  2. 创建所需项目的包装类(消除匿名类型的需要)

这是我代码中唯一需要这样一个集合的地方 - 什么方法最好?

附加信息:

我正在使用旧版的.Net 1.1对象,因此我使用的集合都是实现IEnumerable的强类型集合,因此大多数Linq扩展方法(如.Select)不起作用:

代码示例

foreach (Item item in myItems)
{
    foreach (Confirmation confirmation in item.GetConfirmations(true))
    {
        if (lastParentId != item.ParentId)
        {
            lastParentId = item.ParentId;
            parentObject = new ParentItem(lastParentId);
        }

        itemHolder = new ArrayList { item, confirmation };
        if (parentObject!= null)
        {
            itemHolder.Add(parentObject.Rate);
        }

        sortConfirmations.Add(confirmation.Date, itemHolder);
    }
}

// Use sortConfirmations

分辨率

我最终使用了一个通用字典来将相关项目收集在一起,并使用.OrderBy()进行排序 - 这很丑陋,但它能够工作...而且比现有的代码更好。


请注意:如果要在旧集合中使用LINQ,请使用.Cast<T>(),例如myItems.Cast<Item>()等。 - Marc Gravell
7个回答

27

如果你对一个数组满意,你可以使用数组初始化器:

var items = new[] {
    new { Foo = "def" },
    new { Foo = "ghi" },
    new { Foo = "jkl" }
};
如果您想得到一个List<T>,则可以调用ToList()。由于不需要额外的复制,Marc的解决方案比调用ToList()略微更有效率,但我认为在大多数情况下我可能会使用“数组然后 ToList()”的解决方案,因为它更简洁。当然,如果性能对于这一部分代码非常关键,那就另当别论。
编辑:由于您要遍历一个集合,所以只需使用Select即可:
var anonymousItems = items.Select (item => new { Foo=item.Foo, 
                                                 Bar=item.Other })
                          .ToList();

无法这样做,因为我正在迭代现有集合以检索要放入匿名类型中的对象。我的意思是使用collection.Add(anonType)。 - Oded
一个小提示:数组是只读的,所以如果使用这种方法,请考虑调用.ToList()来生成可写集合。这在单元测试场景中非常方便,例如扩展方法。 - STW

7

对于完全不同的答案……LINQ?

在我的情况下,我正在迭代一个集合 - 对于集合中的每个项目,我想收集匿名类型中的相关对象(充当元组)。 我需要将这些匿名类型放入一个集合中,然后进行排序以进行进一步的工作。

听起来像是:

var list = (from item in collection
          from related in item.Relationship
          order by ...something...
          select new {item,related}).ToList()

不幸的是,我需要实例化一些将在匿名类型中收集的对象。我正在转换一些现有代码,该代码将这些对象放入ArrayList中,并将每个ArrayList放入SorteList中。我需要实现类似但更简洁的功能。 - Oded
@Oded - 这通常仍然是可能的。当然,没有具体的例子很难确定... - Marc Gravell
+1 对于“我们需要更多信息”一点。我强烈怀疑LINQ在这里是前进的方向。 - Jon Skeet
它有帮助。谢谢。 - Topman

4

一种选择是使用示例项目通过通用类型推断来创建集合,例如:

    static List<T> CreateEmptyList<T>(T template) {
        return new List<T>();
    }
    void Foo() {
        var list = CreateEmptyList(new { Foo = "abc" });
        list.Add(new { Foo = "def" });
        //...
    }

您也可以不创建实例,而是使用一个从未被调用的匿名方法来完成此操作,但我认为这并没有真正节省什么...我们可能不会创建对象的实例,但我们确实创建了委托的实例,因此并没有太多节省。您还提到了对数据进行排序;请参阅此处的回复,以了解如何使用lambda和匿名类型来使用List<T>.Sort方法。
list.Sort(x=>x.Foo);

1

LINQ是一个不错的选择。假设您想要获取集合items中每个项目的相关对象A、B和C,并按相关对象A进行排序,您可以按照以下步骤进行。

var relatedObjects = items.
       Select(item => new { A = item.A, B = item.B, C = item.C } ).
       OrderBy(item => item.A);

您将获得一个匿名类型的项目集合,其中三个属性A、B和C设置为相关对象,并按相关对象A排序。

我没有验证过,但您应该能够稍后扩展relatedObject集合。只需执行以下操作即可添加单个新项。如果每个属性的名称和类型匹配,则应该可以工作,因为每个程序集只有一个匿名类型。

relatedObjects = relatedObjects.Union(
   Enumerable.Repeat(new { A = someA, B = someB, C = someC }, 1))

使用Enumerable.Repeat()会让代码看起来相当丑陋,但如果你使用一个新项目的集合进行联合,而不是单个新项目,它就会变得相当不错。


1

在寻找创建临时对象列表的方法时,我偶然发现了这个问题。

为什么要创建这样一个东西呢?我正在寻找一种快速返回JSON的方法,以获取从另一个对象中提取出的对象列表。以下是我的意思。

假设我在服务器端有一个Employee类,其在C#中如下所示:

public class Employee
{
          public int Id;
       public int businessGroupId;
       public string title;
       public int positionId;

       public string firstName;
       public string lastName;
       ...
}

此外,我有一个对象的集合,我可以轻松地进行迭代,类似于以下内容:
List<Employee> allEmployees  = new List<Employee>();

现在,我想将所有员工的完整集合作为JSON返回到我的调用网页,但我只想要每个员工的firstName和lastName

这里是重点

我不想返回Employee对象中的每个属性。

我知道我可以通过遍历allEmployees集合并每次构建一个新对象来轻松创建匿名类型。

它看起来会像以下内容:

foreach (Employee e in allEmployees)
{
    var tempObj = new {F_Name=e.firstName, L_Name=e.lastName};
}

匿名类型的类型是什么? :)

现在的问题是,我想要一个完整的匿名对象集合(List<>)。但是,我没有办法为将要使用的类型提供List。我无法创建一个List<unknown> allTempObjects = new List<unknown>();

或者我可以吗?

使用dynamic,是可能的

我可以做到以下的操作,它很好用:

List<dynamic> allEmployees  = new List<dynamic>();

foreach (Employee e in allEmployees)
{
           var tempObj = new {F_Name=e.firstName, L_Name=e.lastName};
        allEmployees.Add(tempObj);
}

// use the JavaScript Serializer to serialize the dynamic collection
JavaScriptSerializer jss = new JavaScriptSerializer();
// write the result out to the HTTP Stream (HtmlTextWriter in overridden Render method)

 writer.Write(jss.Serialize(allEmployees));

你会在客户端看到类似以下这样的JSON代码:
[
    {
      F_Name: "Seth",
      L_Name: "Godin"
    },
    {
       F_Name: "Charles",
       L_Name: "Petzold"
    },
    {
       F_Name: "Jeff",
       L_Name: "Prosise"
    }
]

最后,你可能会问我为什么要这样做。简单的答案是,我想要一个新的类型,仅用于序列化并发送给我的客户端。一个匿名类型和一个匿名类型集合的绝佳理由,不是吗?

0

这个问题和大部分回答都假设整个匿名集合可以在一个语句中构建。

是的,通常你会在单个语句中创建一个匿名类型的集合。如果你需要更多的语句,你可能最终会得到一个相当无用的不同匿名类型的集合。

当你在单个语句中创建它们并立即使用时,匿名类型非常有用。如果集合的创建或进一步处理稍微复杂一些,匿名类型就会失去一些它的有用性。例如,你不能有效地将匿名类型传递给一个方法进行处理,因为该方法不知道对象的类型,因此必须使用反射来获取其中的任何内容。

如果你要重复查找这个匿名类型集合中的项目,你可能需要创建一个命名类型,以便你可以将它们放入字典中,以便更快地查找。


一个类型化的集合,即使是匿名类型,也不会遭受“不同匿名类型的集合”的命运;你不能把“new {Foo="acb"}”放在“new {Bar [int]}”的集合中。 - Marc Gravell
@Marc Gravell:是的,当然,您需要像List<object>这样的东西来处理两种类型,从而失去与匿名类型信息的直接连接。 - Guffa

-1
你可以像这样创建集合:

private IList<(string 名称,bool 是否监听队列)> _listeners;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接