请考虑以下代码片段:
private List<object> MyList;
public List<object> SubSetOfList
{
get { return MyList.Where(p => p.Property == SomeValue).ToList(); }
}
这是访问我感兴趣的列表子集的一种非常方便的方法。但是,我想它在性能方面可能无法很好地扩展。
我考虑的另一种模式如下。不过我不关心这个,因为它在复杂性方面存在扩展问题,因为我感兴趣的子集数量增加。
private List<object> _myList;
public List<object> WholeList
{
get { return _myList; }
}
private List<object> _valueAList;
public List<object> ValueAList
{
get { return _valueAList; }
}
private List<object> _valueBList;
public List<object> ValueBList
{
get { return _valueBList; }
}
public void AddItem(object obj)
{
_myList.Add(obj);
if (obj.SomePropety == valueA)
_valueAList.Add(obj);
if (obj.SomePropety == valueB)
_valueBList.Add(obj);
}
是否有一种或多种普遍接受的模式来处理这种行为?
这完全取决于您的要求。如果您有某种存储需求,您的第二个解决方案确实会扩展得很差。如果您有性能要求,则始终创建列表也可能缩放不良。
一个可能的解决方案是使用您的第一个解决方案,但存储结果列表并仅在访问时才创建它,例如
private List<object> MyList;
private List<object> _subList;
public List<object> SubSetOfList
{
get { return _subList ?? (_subList = MyList.Where(p => p.Property == SomeValue).ToList();) }
}
这是一种懒惰的评估,因为它只做一次。您当然需要自己处理一致性,因为更改 MyList 需要使存储的子列表无效。
另一种解决方案是返回一个延迟计算的IEnumerable<object>
。但请记住,这种类型的每个用户都需要一遍又一遍地重新评估它。如果你的操作包含许多First()
、Last()
等,那么这实际上会更快,因为以前没有人评估过整个列表,就像(@D Stanley 提到的那样)。
但请注意,人们倾向于评估IEnumerables
有时不止一次,从而导致明确的性能损失。 例如调用 Any(),然后调用 IEnumerable
的其他调用。然后调用者应该正确使用 ToList 等来仅评估列表一次。
还有一个重复:没有分析就没有性能声明
如前所述,在根据合理的替代方案进行衡量之前,不要假设任何事情都是性能问题。 也就是说,一些可能提高性能的选项是:
- 将列表公开为
IEnumerable
s 而不是List
以利用延迟执行。 (例如,像First
这样的操作会更快,因为您不需要水合整个列表)
缓存 - 筛选的列表(确保在添加新项或更改影响列表的属性时清除缓存)
- 使用字典改进按属性查找(在添加项目或更改属性时再次维护字典)
- 按照您的建议维护单独的列表。
您必须决定性能改进是否值得额外的维护。 就个人而言,我会从一个更简单的解决方案开始,只有在改进证明风险和工作合理的情况下才进行重构。