1

我一直在分析我的代码,发现它System.Array.IndexOf分配了相当多的内存。我一直在试图找出这是怎么发生的。

探查器图像

public struct LRItem
{
    public ProductionRule Rule { get; } // ProductionRule is a class
    public int Position { get; }
}

// ...

public List<LRItem> Items { get; } = new List<LRItem>();

// ...

public bool Add(LRItem item)
{
    if (Items.Contains(item)) return false;

    Items.Add(item);
    return true;
}

我假设IndexOf被调用是Items.Contains因为我认为Items.Add没有任何业务检查索引。我尝试查看参考源.NET Core 源,但无济于事。这是 VS 分析器中的错误吗?这个函数实际上是在分配内存吗?我可以以某种方式优化我的代码吗?

4

2 回答 2

2

我知道这可能有点晚了,但万一其他人有同样的问题......

调用时List<T>.Contains(...),它使用EqualityComparer<T>.Default比较各个项目以查找您传入的内容[1]。文档是这样说的EqualityComparer<T>.Default

Default 属性检查类型 T 是否实现了 System.IEquatable 接口,如果是,则返回使用该实现的 EqualityComparer。否则,它返回一个使用 T 提供的 Object.Equals 和 Object.GetHashCode 覆盖的 EqualityComparer。

既然你LRItem没有实现,那么IEquatable<T>它就回退到使用Object.Equals(object, object)。因为LRItem是一个结构,所以它最终会被装箱为一个object,所以它可以传递给Object.Equals(...),这是分配的来源。

解决此问题的简单方法是从文档中获取提示并实现IEquatable<T>接口:

public struct LRItem : IEquatable<LRItem>
{
    // ...

    public bool Equals(LRItem other)
    {
        // Implement this
        return true;
    }
}

现在这将导致EqualityComparer<T>.Default返回一个不需要对LRItem结构进行装箱的专用比较器,从而避免分配。

[1] 我不确定自从提出这个问题以来是否发生了一些变化(或者可能是 .net 框架与核心差异之类的东西),但现在List<T>.Contains()没有调用Array.IndexOf()。无论哪种方式,它们遵循EqualityComparer<T>.Default,这意味着这在任何一种情况下都应该是相关的。

于 2020-09-10T11:53:49.870 回答
-1

您的对象不是像 text 或 number 这样的简单数据类型,因此检查每个项目中的复杂对象可能是内存杀手。

例如,如果您的对象包含图像、文本、数字和......每个数据都应该进行相似性处理。

我建议您不要使用 IndexOf 或 Contains 函数,因为它们 可能会比较对象中的每一个数据。

只需手动执行,使用单个 foreach 循环并比较您的关键数据(用户 ID、对象 ID、姓名、家庭、日期、时间或...)。

于 2018-09-08T10:46:22.410 回答