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<> 的相关文章

  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • 使用.Net/C# 计算集合的频率分布

    是否有一种快速 简单的方法来使用 Linq 或其他方式计算 Net 集合的频率分布 例如 任意长的 List 包含许多重复项 遍历列表并计算 跟踪重复次数的巧妙方法是什么 查找列表中重复项的最简单方法是将其分组 如下所示 var dups
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • C++ 中的 include 和 using 命名空间

    用于使用cout 我需要指定两者 include
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐