C# Ordered enumerable - 对象引用未设置为对象的实例

C# Ordered enumerable - An object reference is not set to an instance of an object

我有以下代码:

// OnlineAccountCategory is a class
var itemsByCategory = Items.GroupBy(x => x.OnlineAccountCategory); 
foreach (var items in itemsByCategory)
...

Items 是一个包含 1 个项目的列表。项目有一个 OnlineAccountCategory 而不是 null。但是当我尝试通过 itemsByCategory 时,我收到 NullReferenceException。在调试模式下,我可以看到 itemsByCategory 是一个 GroupedEnumerable,但是如果我在其中展开结果视图,它会显示 'An object reference is not set to an instance of an object'.

我不太明白这段代码有什么问题,因为 GroupBy() 没有任何异常地正常运行,但随后在 foreach 循环中中断。

这里的问题是 OnlineAccountCategory 是 reference 类型 属性(而不是字符串或类似的东西)。按引用类型分组使用 class 的方法 GetHashCode() 进行分组。

关键是如果 GetHashCode() 方法抛出异常(例如,如果它依赖于我们对象的 属性,即 nullGroupBy() 方法不会失败。它只是 return 一个 GroupedEnumerable 出于某种原因在内部包含一个异常。

// Trying to add this to watch:     
Items.First().OnlineAccountCategory.GetHashCode()   
// Receiving: 
'Items.First().OnlineAccountCategory.GetHashCode()' threw an exception of type 
'System.NullReferenceException' int {System.NullReferenceException}

因此,当我们尝试访问集合中的元素时,它落在 NullReferenceException

为了解决这个问题,我们有两种方法:

  1. 覆盖我们OnlineAccountCategoryGetHashCode方法 所以它不会因不正确的值而失败,而不是抛出一个 异常将 return 类似于 0.

  2. 改为按某些值类型分组(例如, OnlineAccountCategory.Id)。它会按它分组,因此不会 失败。

更新:GetHashCode() 方法已经被错误地实现时(在我的例子中,它是在基础 class 中实现的),问题将 发生。默认参考比较器将按预期工作。

我的 GetHashCode() 方法如下所示:

public override int GetHashCode()
{
    // Title is a string
    return Title != null ? Title.GetHashCode() : 0;
}

所以我真的不明白为什么它会在这里出现 NullReferenceException。

很高兴您解决了问题,但我认为您没有完全理解错误的原因。

I don't quite understand what's the problem with this code as GroupBy() goes fine without any exception but then breaks in the foreach loop.

在执行 foreach 之前您看不到错误的原因是 延迟执行 。在您尝试枚举集合 之前,GetHashCode 不会被调用 。如果 GetHashCode 抛出 NullReferenceException,那么在使用 foreach 执行查询之前,您不会知道这一点,也就是实际创建组时。

当然 root 原因是您的 GetHashCode 方法不正确,这很容易在单元测试中检测到。

请注意,在不覆盖 GetHashCode 的情况下按引用类型分组非常好,因为任何空值都将被放入具有 null 键的组中(Linq 不会尝试调用 GetHashCode 在空引用上)。当您想根据实例的值为不同的实例定义相等性而不是默认使用引用相等性时,您只需要 覆盖GetHashCode