System.Array.IndexOf分配内存

时间:2018-09-08 09:34:47

标签: c# .net-core profiling system.array

我一直在分析代码,发现System.Array.IndexOf正在分配相当大的内存。我一直在尝试找出这种情况。

profiler image

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;
}

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

2 个答案:

答案 0 :(得分:1)

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

调用List<T>.Contains(...)时,它将使用EqualityComparer<T>.Default比较各个项目以查找您传入的内容[1]。 docs说关于EqualityComparer<T>.Default的话:

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

由于您的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()。无论哪种方式,它们都 do 遵从EqualityComparer<T>.Default,这意味着这两种情况在任何情况下都仍然适用。

答案 1 :(得分:-1)

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

例如,如果您的对象包含图像,文本,数字和...,则每个数据都应经过相似处理。

我建议您请勿使用IndexOf或Contains函数,因为它们 可以比较对象中的每个数据。

只需手动完成一次,只需一个foreach循环,然后 just 比较您的密钥 数据(用户ID,对象ID,名称,家庭,日期,时间或...)。