我一直在分析代码,发现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
没有任何业务检查索引。我尝试查看reference source和.NET Core source,但无济于事。这是VS Profiler中的错误吗?该函数实际上是在分配内存吗?我可以以某种方式优化我的代码吗?
答案 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,名称,家庭,日期,时间或...)。