Linq 的 Enumerable.Count 方法检查 ICollection<> 但不检查 IReadOnlyCollection<>

2023-12-31

背景:

Linq-To-Objects 具有扩展名method Count() http://msdn.microsoft.com/en-us/library/bb338038.aspx(不带谓词的重载)。当然,有时当一个方法只需要一个IEnumerable<out T>(做 Linq),我们真的会传递一个“更丰富”的对象给它,比如ICollection<T>。在这种情况下,实际迭代整个集合(即获取枚举器并“移动下一个”很多次)来确定计数是浪费的,因为有一个财产ICollection<T>.Count http://msdn.microsoft.com/en-us/library/5s3kzhec.aspx以此目的。而这个“快捷方式”从Linq开始就一直在BCL中使用。

现在,自 .NET 4.5(2012 年)以来,还有另一个非常好的界面,即IReadOnlyCollection<out T>。它就像ICollection<T>但它只包括那些成员return a T。因此它可以是协变的T ("out T“), 就像IEnumerable<out T>,当项目类型可以或多或少派生时,这真的很好。但新的界面有它自己的属性,IReadOnlyCollection<out T>.Count http://msdn.microsoft.com/en-us/library/hh881496.aspx。参见其他地方那么为什么这些Count属性是不同的(而不仅仅是一个属性) https://stackoverflow.com/questions/12622539/.

问题:

Linq 的方法Enumerable.Count(this source)确实检查ICollection<T>.Count,但它不检查IReadOnlyCollection<out T>.Count.

鉴于在只读集合上使用 Linq 确实很自然且很常见,更改 BCL 来检查两个接口是个好主意吗?我想这需要一项额外的类型检查。

这会是一个重大变化吗(假设他们没有“记得”从引入新界面的 4.5 版本开始执行此操作)?

示例代码

运行代码:

    var x = new MyColl();
    if (x.Count() == 1000000000)
    {
    }

    var y = new MyOtherColl();
    if (y.Count() == 1000000000)
    {
    }

where MyColl是一种实现类型IReadOnlyCollection<>但不是ICollection<>,以及哪里MyOtherColl是一种实现类型ICollection<>。具体来说,我使用了简单/最小的类:

class MyColl : IReadOnlyCollection<Guid>
{
  public int Count
  {
    get
    {
      Console.WriteLine("MyColl.Count called");
      // Just for testing, implementation irrelevant:
      return 0;
    }
  }

  public IEnumerator<Guid> GetEnumerator()
  {
    Console.WriteLine("MyColl.GetEnumerator called");
    // Just for testing, implementation irrelevant:
    return ((IReadOnlyCollection<Guid>)(new Guid[] { })).GetEnumerator();
  }

  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
    Console.WriteLine("MyColl.System.Collections.IEnumerable.GetEnumerator called");
    return GetEnumerator();
  }
}
class MyOtherColl : ICollection<Guid>
{
  public int Count
  {
    get
    {
      Console.WriteLine("MyOtherColl.Count called");
      // Just for testing, implementation irrelevant:
      return 0;
    }
  }

  public bool IsReadOnly
  {
    get
    {
      return true;
    }
  }

  public IEnumerator<Guid> GetEnumerator()
  {
    Console.WriteLine("MyOtherColl.GetEnumerator called");
    // Just for testing, implementation irrelevant:
    return ((IReadOnlyCollection<Guid>)(new Guid[] { })).GetEnumerator();
  }

  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
    Console.WriteLine("MyOtherColl.System.Collections.IEnumerable.GetEnumerator called");
    return GetEnumerator();
  }

  public bool Contains(Guid item) { throw new NotImplementedException(); }
  public void CopyTo(Guid[] array, int arrayIndex) { throw new NotImplementedException(); }
  public bool Remove(Guid item) { throw new NotSupportedException(); }
  public void Add(Guid item) { throw new NotSupportedException(); }
  public void Clear() { throw new NotSupportedException(); }
}

并得到输出:


MyColl.GetEnumerator called
MyOtherColl.Count called  

从代码运行来看,这表明在第一种情况下没有使用“快捷方式”(IReadOnlyCollection<out T>)。 4.5 和 4.5.1 中看到相同的结果。


UPDATE用户在 Stack Overflow 上其他地方发表评论后supercat.

当然,Linq 是在 .NET 3.5 (2008) 中引入的,IReadOnlyCollection<>仅在 .NET 4.5 (2012) 中引入。然而,在这两者之间,还有一个特点,泛型中的协方差在 .NET 4.0 (2010) 中引入。正如我上面所说,IEnumerable<out T>成为协变接口。但ICollection<T>保持不变T(因为它包含像void Add(T item);).

早在 2010 年(.NET 4),这就产生了这样的后果:如果 Linq 的Count扩展方法用于编译时类型的源IEnumerable<Animal>例如,实际运行时类型是List<Cat>,说,这肯定是一个IEnumerable<Cat>而且,通过协方差,IEnumerable<Animal>,那么“捷径”就是not用过的。这Count扩展方法仅检查运行时类型是否为ICollection<Animal>,但事实并非如此(无协方差)。它无法检查ICollection<Cat>(它怎么知道什么是Cat是它的TSource参数等于Animal?).

让我举个例子吧:

static void ProcessAnimals(IEnuemrable<Animal> animals)
{
    int count = animals.Count();  // Linq extension Enumerable.Count<Animal>(animals)
    // ...
}

then:

List<Animal> li1 = GetSome_HUGE_ListOfAnimals();
ProcessAnimals(li1);  // fine, will use shortcut to ICollection<Animal>.Count property

List<Cat> li2 = GetSome_HUGE_ListOfCats();
ProcessAnimals(li2);  // works, but inoptimal, will iterate through entire List<> to find count

我建议检查IReadOnlyCollection<out T>也会“修复”这个问题,因为这是一个协变接口,它是由List<T>.

结论:

  1. 还检查IReadOnlyCollection<TSource>在运行时类型的情况下将是有益的source实施IReadOnlyCollection<>但不是ICollection<>因为底层集合类坚持是只读集合类型,因此希望not实施ICollection<>.
  2. (新)还检查IReadOnlyCollection<TSource>即使当类型source既是ICollection<> and IReadOnlyCollection<>,如果通用协方差适用。具体来说,IEnumerable<TSource>可能真的是一个ICollection<SomeSpecializedSourceClass> where SomeSpecializedSourceClass可通过引用转换为TSource. ICollection<>不是协变的。然而,检查IReadOnlyCollection<TSource>将通过协方差起作用;任何IReadOnlyCollection<SomeSpecializedSourceClass>也是一个IReadOnlyCollection<TSource>,并且将使用快捷方式。
  3. 成本是每次调用 Linq 时进行一次额外的运行时类型检查Count method.

在许多情况下,一个类实现IReadOnlyCollection<T>也将实施ICollection<T>。因此,您仍然可以从 Count 属性快捷方式中受益。

See 只读集合 http://msdn.microsoft.com/en-us/library/ms132474.aspx?queryresult=true例如。

public class ReadOnlyCollection<T> : IList<T>, 
    ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, 
    IEnumerable<T>, IEnumerable

由于检查其他接口以获取超出给定只读接口的访问权限是不好的做法,因此这种方式应该没问题。

实施额外的类型检查IReadOnlyInterface<T> in Count()对于未实现的对象的每次调用都将是额外的镇流器IReadOnlyInterface<T>.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linq 的 Enumerable.Count 方法检查 ICollection<> 但不检查 IReadOnlyCollection<> 的相关文章

  • 实体框架代码优先 - 在另一个文件中配置

    使用 Fluent API 将表到实体的映射分开的最佳方法是什么 以便它全部位于单独的类中 而不是内联在 OnModelCreating 方法中 我目前在做什么 public class FooContext DbContext prote
  • .NET 可移植类库中的 .ToShortDateString 发生了什么

    我想知道为什么没有 ToShortDateString在 NET 可移植类库中 我有 2 个项目 Silverlight 和常规 NET 类库 使用相同的代码 并且代码涉及调用 ToShortDateString on a DateTime
  • 有没有比这更快的方法来查找目录和所有子目录中的所有文件?

    我正在编写一个程序 需要在目录及其所有子目录中搜索具有特定扩展名的文件 这将在本地驱动器和网络驱动器上使用 因此性能是一个问题 这是我现在使用的递归方法 private void GetFileList string fileSearchP
  • 使用 C# 使用应用程序密码登录 Office 365 SMTP

    在我们的 Office 365 公司帐户中实施两步身份验证之前 我的 C WPF 程序已成功进行身份验证并发送邮件 我使用了 SmtpClient 库 但现在我必须找到另一个解决方案 因为它不再起作用 我找不到任何使用 O365 应用程序密
  • 浮点提升:stroustrup vs 编译器 - 谁是对的?

    在 Stroustrup 的新书 C 编程语言 第四版 第 10 5 1 节中 他说 在执行算术运算之前 整数提升用于从较短的整数类型创建整数 类似地 浮点提升是用于从浮点数创建双精度数 我用以下代码确认了第一个声明 include
  • 对数字进行向上和向下舍入 C++

    我试图让我的程序分别向上和向下舍入数字 例如 如果数字是3 6 我的程序应该四舍五入最接近的数字 4 如果该数字是3 4 它将向下舍入为 3 我尝试使用ceil库获取 3 个项目的平均值 results ceil marks1 marks2
  • 如何避免选择项目时 winforms 树视图图标发生变化

    我正在一个小型 C Winforms 应用程序中尝试树视图 我已经以编程方式将 ImageList 分配给树视图 并且所有节点都很好地显示了它们的图标 but当我单击一个节点时 它的图标会发生变化 变为 ImageList 中的第一个图像
  • 通过 mpi 发送 c++ std::vector

    我知道存储一个std vector
  • 捕获当前正在播放的声音

    是否可以捕获计算机上当前播放的声音 如果能够将其保存为 mp3 就好了 但我认为这样做会存在一些法律问题 所以 wav 也可以 我环顾四周 有人建议使用虚拟音频线之类的东西 在 C 中捕获声音输出 https stackoverflow c
  • 我的 C# .NET 团队是否应该迁移到 Windows Presentation Foundation? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • for 循环 - 没有效果的语句

    由于某种原因 我收到错误 statement with no effect关于这个声明 for j idx j lt iter j increment printf from loop idx i int idx punc ctxt j 你
  • 当格式字符串包含“{”时,String.Format 异常

    我正在使用 VSTS 2008 C Net 2 0 执行以下语句时 String Format 语句抛出 FormatException 有什么想法是错误的吗 这是获取我正在使用的 template html 的位置 我想在 templat
  • 在可观察项目生成时对其进行处理

    我有一个IObservable它会生成一次性物品 并且在其生命周期内可能会生成无限数量的物品 因此 我想在每次生成新项目时处理最后一个项目 因此Using http reactivex io documentation operators
  • 使用 AutoMapper 进行 LINQ GroupBy 聚合

    试图让查询工作 但老实说不确定如何 或者是否可能 进行它 因为我尝试过的一切都不起作用 共查询6个表 Person PersonVote PersonCategory Category City FirstAdminDivision Per
  • 如何在 C++ 中使用 PI 常数

    我想在一些 C 程序中使用 PI 常数和三角函数 我得到三角函数include
  • 编写专门用于类及其子类的函数模板

    我正在尝试编写一个函数模板 一个版本应该用于不满足另一版本标准的所有类型 当参数是给定类的基类或该类本身时 应使用另一个版本 我尝试过超载Base 但是当类派生自Base 他们使用通用的 而不是特定的 我也尝试过这种 SFINAE 方法 s
  • 便携式终端

    有没有办法根据所使用的操作系统自动使用正确的 EOL 字符 我在想类似的事情std eol 我知道使用预处理器指令非常容易 但很好奇它是否已经可用 我感兴趣的是 我的应用程序中通常有一些消息 稍后我会将这些消息组合成一个字符串 并且我希望将
  • 在 MVVM 中,可以在视图后面的代码中访问 ViewModel 吗?

    在 MVVM 模式中 是否可以接受甚至可以访问视图代码后面的 ViewModel 属性 我有一个可观察的集合 它填充在 ViewModel 中 我需要在视图中使用它来绑定到带有链接列表的无限滚动条 IE private LinkedList
  • C# - 为什么我需要初始化 [Out] 参数

    我有几个从本机 dll 导入的方法 使用以下语法 internal static class DllClass DllImport Example dll EntryPoint ExampleFunction public static e
  • 从 C# 中的 .NET SecureString 读取单个字符?

    WPF 的PasswordBox 返回一个SecureString 它对窥探者隐藏密码 问题是你最终必须获得密码的值 而我在网上找到的建议都涉及将值复制到字符串中 这会让你回到窥探者的问题 IntPtr bstr Marshal Secur

随机推荐