You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何派生类虚重写的迭代器版RemoveRange未被调用?

问题

在为使用Entity Framework的类编写单元测试时,基于微软文档实现了TestDbSet<T>测试替身(继承自DbSet<T>),大部分测试正常,但测试依赖DbSet<T>.RemoveRange(IEnumerable<T>)的代码时遇到异常:

最初重写的方法使用了yield return迭代器:

public override IEnumerable<TEntity> RemoveRange(IEnumerable<TEntity> entities)
{
    foreach (var item in entities)
    {
        Data.Remove(item);
        yield return item;
    }
}

但将TestDbSet<T>实例赋值给DbSet<T>变量并调用RemoveRange时,并未执行上述重写方法,而是调用了基类实现。确认实例类型正确且其他重写方法正常后,将方法改为非迭代器版本:

public override IEnumerable<TEntity> RemoveRange(IEnumerable<TEntity> entities)
{
    entities = entities.ToList();
    foreach (var item in entities)
    {
        Data.Remove(item);
    }
    return entities;
}

此时调用DbSet<T>变量的RemoveRange会正常执行TestDbSet<T>的重写方法。仅移除yield return就能解决问题,想了解:迭代器方法是否会改变方法声明?为何它能与override兼容?

原因分析
  • 迭代器的延迟执行是核心问题:用yield return的迭代器方法,编译后会被转换成一个实现了IEnumerable<T>/IEnumerator<T>的状态机类。方法本身不会立刻执行内部的删除逻辑——只有当调用方枚举返回的集合(比如调用ToList()、用foreach遍历)时,状态机才会逐步执行迭代器里的代码。而业务代码调用RemoveRange时,通常不会去枚举返回值,导致你的Data.Remove(item)根本没执行,看起来像是走了基类方法。
  • 基类方法的执行逻辑要求:EF的DbSet<T>基类中,RemoveRange是即时执行修改操作的。你的迭代器版本因为延迟执行特性,调用方法的瞬间没有做任何实际删除操作,这和基类的预期行为不符,导致测试中出现“没执行重写方法”的假象——实际上方法确实被调用了,但核心的删除逻辑没触发。
  • 迭代器和override的兼容性没问题:迭代器方法的签名(返回值、参数列表)和普通方法完全一致,编译器会确保它符合override的规则(匹配基类方法的签名),所以语法上完全兼容。问题不在方法声明本身,而在执行时机的差异。

内容的提问来源于stack exchange,提问作者Shelby Oldfield

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 01:25:27