简体   繁体   English

如何在C#中从优雅集合中创建一个空集合?

[英]How can I create an empty collection from an anonymous collection most elegantly in C#?

If I have an anonymous type created by LINQ 如果我有一个由LINQ创建的匿名类型

var ans = from r in someList where someCondition(r) select new { r.a, r.b };

What is the best way to create an empty matching collection so I can move elements to the new collection: 创建空匹配集合的最佳方法是什么,这样我就可以将元素移动到新集合:

var newans = ?
foreach (r in ans) { if (complicated(r)) newans.Add(r); }

Is there some way to use Enumerable.Empty<>()? 有什么方法可以使用Enumerable.Empty <>()吗?

I wouldn't use the ans.Where(x => false).ToList() versions these iterate over all elements, which you don't need; 我不会使用ans.Where(x => false).ToList()版本来遍历所有不需要的元素; ans.Take(0).ToList() is a little better, as this one doesn't actually iterate over the entire list when you're querying over in-memory sequences (LINQ to Objects). ans.Take(0).ToList()一点,因为当您查询内存中的序列(LINQ to Objects)时,它实际上并没有遍历整个列表。

If you are using LINQ to SomethingElse, things are a little problematic, because it might actually execute something like SELECT TOP(0) * FROM ... or SELECT * FROM ... WHERE 1 = 0 . 如果您正在使用LINQ to SomethingElse,事情会有些问题,因为它实际上可能会执行类似SELECT TOP(0) * FROM ...SELECT * FROM ... WHERE 1 = 0 Not good. 不好。

So the following helper method will help you: 因此,以下帮助程序方法将为您提供帮助:

public static class AnonymousTypeExtensions
{
    public static List<T> ToEmptyList<T>(this IEnumerable<T> source)
    {
        return new List<T>();
    }
}

var newans = ans.ToEmptyList();

Having said that, if your only desire is to copy some elements of ans into a new list, you're better of with 话虽如此,如果您唯一的愿望是将ans某些元素复制到新列表中,那么最好使用

var newans = (from a in ans where isComplicated(a) select a).ToList();

or 要么

var newans = ans.Where(a => isComplicated(a)).ToList();

If you don't need an actual List<T> , and an IEnumerable<T> is enough, you might get away with omitting the ToList altogether. 如果您不需要实际的List<T> ,而IEnumerable<T>就足够了,那么您可以完全省去ToList了。 Mind you, every time you foreach over such an enumerable, Where(a => isComplicated(a)) is executed again and again. 提醒您,每次在这样的枚举上进行遍历时,都会一次又一次执行Where(a => isComplicated(a)) The call to ToList makes sure you only execute it once. 调用ToList确保只执行一次。


If you don't want this just for List<T> , but more general, things get a lot more complicated. 如果您不只是想要List<T> ,而是更一般的话,事情就会变得复杂得多。 For example, should the method return the static type of the ans variable or its runtime type? 例如,该方法是否应返回ans变量的静态类型或其运行时类型? Also, there is no such thing as a "default" version of many collections, apart from blindly calling new C() . 此外,除了盲目调用new C()之外,没有许多集合的“默认”版本。 But that won't work generally. 但这通常不会起作用。 It's not possible to call new ReadOnlyCollection<T>() , because it doesn't exist (and the collection would be sealed anyway, so it cannot be filled). 不可能调用new ReadOnlyCollection<T>() ,因为它不存在(而且该集合将被密封,因此无法填充)。 And if you're using a HasSet, shouldn't you be using the original's comparer for your empty copy rather than the default comparer? 而且,如果您使用的是HasSet,是否不应该将原始的比较器用于空副本,而不是默认的比较器?

However, for the simplest case, you could try: 但是,对于最简单的情况,您可以尝试:

public static class CollectionExtensions
{
    public static TCollection AsEmpty<TCollection>(this TCollection source)
        where TCollection : ICollection, new()
    {
        return new TCollection();
    }
}

Note that this is a compile-time solution which returns a default collection of the variable 's type. 请注意,这是一个编译时解决方案,它返回变量类型的默认集合。 If at all possible. 如果可能的话。 For instance ans = from r in somesource select new { ... } will have the static type IEnumerable<...> . 例如ans = from r in somesource select new { ... }将具有静态类型IEnumerable<...> There's no default instance here to create. 这里没有默认实例可以创建。 Also, the runtime type returned by select is private to System.Core.dll, doesn't have a default constructor, and is a read-only cursor-like type anyway. 同样, select返回的运行时类型是System.Core.dll的私有类型,没有默认构造函数,并且还是只读的类似游标的类型。

So my opinion on this one is: don't go there. 所以我对此的看法是:不要去那里。 Don't try to over-generalize, stick to simple solutions like ToEmptyList or even better, stick to plain LINQ method chaining. 不要过度概括,坚持使用ToEmptyList之类的简单解决方案,甚至更好,坚持使用简单的LINQ方法链。

I haven't tried it, but 我没有尝试过,但是

var newans = from r in ans where false select r;

seems like a way to generate the right type of IEnumerable (but not a collection you can 'add' to - you could ToList it). 似乎是一种生成正确类型的IEnumerable的方法(但不是可以“添加”到的集合-可以将其列出)。

That said, if you're using LINQ, seems unlikely to have the 'foreach-if-.add' code you have in the question, just 就是说,如果您使用的是LINQ,似乎不太可能拥有问题中包含的“ foreach-if-.add”代码,

var newans = from r in ans where isComplicated(r) select r;

or 要么

var newans = ans.Where(isComplicated);

no? 没有?

var newans = ans.Where(false).ToList() will do it. var newans = ans.Where(false).ToList()将执行此操作。

Enumerable.Empty can't be used because it's not possible to explicitly pass an anonymous type as a generic argument. 无法使用Enumerable.Empty因为无法显式传递匿名类型作为泛型参数。

Use var newans = ans.ToList(); 使用var newans = ans.ToList(); which will copy every enumerated object into a new list. 它将每个枚举的对象复制到一个新列表中。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM