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

访问字典中的Mock Assembly对象时抛出KeyNotFoundException问题排查

问题分析与解决

问题重现

测试代码中使用Moq模拟Assembly类,将模拟对象存入Dictionary<Assembly, HashSet<Type>>后,明明通过Assert.Contains确认键存在,但访问字典值时抛出KeyNotFoundException,甚至调用TryGetValue也返回false。

测试代码:

[Fact]
public void MockAssembly_GetTypes_ReturnsMockedTypes()
{
    var mockAssembly = new Mock<Assembly>();

    mockAssembly.Setup(a => a.GetTypes()).Returns(new Type[] { typeof(MyClass) });
    var mockAssemblyObject = mockAssembly.Object;

    var assemblyDictionary = new Dictionary<Assembly, HashSet<Type>>();
    if (!assemblyDictionary.TryGetValue(mockAssemblyObject, out var types))
    {
        types = new HashSet<Type>(mockAssemblyObject.GetTypes());
        assemblyDictionary[mockAssemblyObject] = types;
    }

    Assert.Contains(mockAssemblyObject, assemblyDictionary.Keys);
    Assert.Single(assemblyDictionary[mockAssemblyObject]);
}

异常信息:

System.Collections.Generic.KeyNotFoundException: The given key '' was not present in the dictionary.

原因

Assembly继承自MarshalByRefObject,这类对象的实例本质是透明代理。Moq生成的Assembly模拟对象,其默认的Equals和GetHashCode方法行为不符合字典的键匹配要求:

  1. 存入字典时使用的是代理对象的初始哈希码,后续访问时GetHashCode返回值可能发生变化;
  2. Equals方法无法正确识别同一个代理实例,导致字典判定键不存在。

仅MockGetHashCode不足以解决问题——字典同时依赖GetHashCode和Equals来确定键的唯一性。

解决方法

方案1:Mock Equals和GetHashCode方法

手动模拟这两个方法,让它们基于引用相等判断:

[Fact]
public void MockAssembly_GetTypes_ReturnsMockedTypes()
{
    var mockAssembly = new Mock<Assembly>();
    var mockAssemblyObject = mockAssembly.Object;

    // 模拟GetTypes方法
    mockAssembly.Setup(a => a.GetTypes()).Returns(new Type[] { typeof(MyClass) });

    // 模拟Equals:基于引用比较
    mockAssembly.Setup(a => a.Equals(It.IsAny<object>())).Returns((object other) => 
        ReferenceEquals(mockAssemblyObject, other));

    // 模拟GetHashCode:返回代理对象的原始哈希码
    mockAssembly.Setup(a => a.GetHashCode()).Returns(mockAssemblyObject.GetHashCode());

    var assemblyDictionary = new Dictionary<Assembly, HashSet<Type>>();
    if (!assemblyDictionary.TryGetValue(mockAssemblyObject, out var types))
    {
        types = new HashSet<Type>(mockAssemblyObject.GetTypes());
        assemblyDictionary[mockAssemblyObject] = types;
    }

    Assert.Contains(mockAssemblyObject, assemblyDictionary.Keys);
    Assert.Single(assemblyDictionary[mockAssemblyObject]);
}

方案2:使用引用比较器

创建字典时传入基于引用比较的IEqualityComparer,无需Mock对象方法:

// .NET Core 2.1+/NET Framework 4.7.2+ 可用内置类型
var assemblyDictionary = new Dictionary<Assembly, HashSet<Type>>(ReferenceEqualityComparer.Instance);

低版本.NET可自行实现引用比较器:

public class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    public bool Equals(T x, T y) => ReferenceEquals(x, y);
    public int GetHashCode(T obj) => obj.GetHashCode();
}

// 使用方式
var assemblyDictionary = new Dictionary<Assembly, HashSet<Type>>(new ReferenceEqualityComparer<Assembly>());

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:35:07