如何在 C# 中最优雅地从匿名集合创建空集合?

发布于 2024-09-02 07:20:46 字数 319 浏览 2 评论 0原文

如果我有一个由 LINQ 创建的匿名类型,

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

那么创建空匹配集合以便我可以将元素移动到新集合的最佳方法是什么:

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

是否有某种方法可以使用 Enumerable.Empty<>()?

If I have an anonymous type created by 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<>()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦里兽 2024-09-09 07:20:46

我不会使用 ans.Where(x => false).ToList() 版本来迭代所有元素,这是您不需要的; ans.Take(0).ToList() 稍微好一些,因为当您查询内存中序列(LINQ to Objects)时,它实际上不会迭代整个列表。

如果您使用 LINQ to SomethingElse,事情会有点问题,因为它实际上可能会执行类似 SELECT TOP(0) * FROM ...SELECT * FROM ... WHERE 1 的内容= 0 。不好。

因此,以下辅助方法将为您提供帮助:

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

var newans = ans.ToEmptyList();

话虽如此,如果您唯一的愿望是将 ans 的某些元素复制到新列表中,那么您最好使用

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

or

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

如果您不需要实际的 ListIEnumerable 就足够了,您可能完全省略 ToList 。请注意,每次 foreach 此类可枚举时,Where(a => isComplicated(a)) 都会一次又一次执行。对 ToList 的调用可确保您只执行一次。


如果您不希望这仅适用于 List,而是更通用,那么事情会变得更加复杂。例如,该方法应该返回 ans 变量的静态类型还是其运行时类型?此外,除了盲目调用 new C() 之外,许多集合不存在“默认”版本。但这一般行不通。无法调用 new ReadOnlyCollection(),因为它不存在(并且集合无论如何都会被密封,因此无法填充)。如果您使用 HasSet,您是否应该对空副本使用原始比较器而不是默认比较器?

但是,对于最简单的情况,您可以尝试:

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

请注意,这是一个编译时解决方案,它返回变量类型的默认集合。如果可能的话。例如,ans = from r in somesource select new { ... } 将具有静态类型 IEnumerable<...>。这里没有要创建的默认实例。此外,select 返回的运行时类型是 System.Core.dll 私有的,没有默认构造函数,并且无论如何都是只读的类似游标的类型。

所以我对此的看法是:不要去那里。不要试图过度概括,坚持使用 ToEmptyList 等简单的解决方案,甚至更好,坚持使用简单的 LINQ 方法链接。

I wouldn't use the ans.Where(x => false).ToList() versions these iterate over all elements, which you don't need; 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).

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. 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

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. Mind you, every time you foreach over such an enumerable, Where(a => isComplicated(a)) is executed again and again. The call to ToList makes sure you only execute it once.


If you don't want this just for List<T>, but more general, things get a lot more complicated. For example, should the method return the static type of the ans variable or its runtime type? Also, there is no such thing as a "default" version of many collections, apart from blindly calling 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). 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?

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<...>. 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.

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.

海拔太高太耀眼 2024-09-09 07:20:46

我还没有尝试过,但

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

似乎是一种生成正确类型的 IEnumerable 的方法(但不是可以“添加”到的集合 - 您可以将其添加到列表中)。

也就是说,如果您使用 LINQ,似乎不太可能有问题中的“foreach-if-.add”代码,只是

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

还是

var newans = ans.Where(isComplicated);

没有?

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).

That said, if you're using LINQ, seems unlikely to have the 'foreach-if-.add' code you have in the question, just

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

or

var newans = ans.Where(isComplicated);

no?

归属感 2024-09-09 07:20:46

var newans = ans.Where(false).ToList() 会做到这一点。

无法使用 Enumerable.Empty,因为无法显式传递匿名类型作为泛型参数。

var newans = ans.Where(false).ToList() will do it.

Enumerable.Empty can't be used because it's not possible to explicitly pass an anonymous type as a generic argument.

家住魔仙堡 2024-09-09 07:20:46

使用 var newans = ans.ToList(); 会将每个枚举对象复制到新列表中。

Use var newans = ans.ToList(); which will copy every enumerated object into a new list.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文