因此,每个关心OOP中的最佳实践并保持代码清洁的人都知道这些方法不应该做多件事。方法是离散的单位,做一件事并离开。
但是,如果您要将两个方法组合在一起,并将第二个方法中已有的现有for循环重新组合使用,那么可以节省一些处理并提高性能,这是一种情况:
private void RemoveDiscontinuedItems()
{
for(int s = 0; s < itemList.Count; s++)
{
if(!itemList[s].ItemIsOnSite)
{
RemoveItem(itemList[s].Id); // remove it from the DB
itemList.RemoveAt(s); // remove it from the collection
s--;
}
}
}
private void RemovePriceChangedItems()
{
for (int s = 0; s < itemList.Count; s++)
{
if(!PricingOptionIsValid(itemList[s]))
{
RemoveItem(itemList[s].Id); // remove it from the DB
itemList.RemoveAt(s); // remove it from the collection
s--;
}
}
}
这些在页面加载时调用。一个删除已停止的项目。另一个删除具有某些已更改的定价选项的项目,并将其从同一列表中删除。
现在,如果我们坚持使用最佳实践,可以说这些是完全独立的两个目的,因此我们不应该将这两种方法中的逻辑结合起来。然后,这将使该方法做两件事,我还必须提出一些新的名称,如RemoveDiscontinuedAndPriceChangeItems()或通用名称,不告诉我杰克sh **像RemoveInvalidItemsFromList():< / p>
private void RemoveDiscontinuedItems()
{
for(int s = 0; s < itemsList.Count; s++)
{
if((!itemList[s].ItemIsOnSite))
{
RemoveItem(orderItemsSavedList[s].Id); // remove it from the DB
itemList.RemoveAt(s); // remove it from the collection
s--;
}
else if(!PricingOptionIsValid(itemList[s]))
{
RemoveItem(itemList[s].Id); // remove it from the DB
itemList.RemoveAt(s); // remove it from the collection
s--;
}
}
然而,考虑到性能方面,调用2个循环遍历同一列表的方法来删除一些项目,在循环中会更加昂贵。
那么,任何反对组合的人还是你在这种情况下合并?想听听那里的一些意见。
答案 0 :(得分:20)
为什么不重构,以便每个方法执行单个操作,而不是执行循环。然后在循环体中根据需要调用每个方法。
<强>更新强> 这是一个基于您的方法的简单示例。显然,在你的应用程序中,Loop方法会有所不同,但是,我没有任何其他上下文来处理你正在做的事情。另外,我将你的for循环改为foreach。
private void Loop()
{
foreach (Item i in itemList)
{
if(!item.ItemIsOnSite)
{
RemoveDiscontinuedItems(i)
}
if(!item.PricingOptionIsValid)
{
RemovePriceChangedItems(i)
}
}
}
private void RemoveDiscontinuedItems(itemType item)
{
RemoveItem(item.Id); // remove it from the DB
item.Remove; // remove it from the collection
}
private void RemovePriceChangedItems(itemType item)
{
RemoveItem(item.Id); // remove it from the DB
item.Remove; // remove it from the collection
}
答案 1 :(得分:7)
我认为你的因素是在错误的地方。
如果你这样写:
public void ConditionallyRemoveItems(Func<Item,bool> predicate)
{
for (int s=0; s < itemsList.Count; s++) {
if (predicate(itemList[s])) {
RemoveItem(orderItemsSavedList[s].Id);
itemList.RemoveAt(s);
s--;
}
}
}
// ...
ConditionallyRemoveItems(i => !i.ItemIsOnSize || !PricingOptionIsValid(i));
我也不太喜欢你弄乱循环变量的风格 - 我更喜欢这种风格:
List<Item> itemsToRemove = new List<Items>();
foreach (Item i in itemList) {
if (predicate(i)) {
itemsToRemove.Add(i);
}
}
foreach (Item i in itemsToRemove)
itemList.Remove(i);
如果你不喜欢它的表现,你可以随时这样做:
List<Item> itemsToKeep = new List<Items>();
foreach (Item i in itemList) {
if (!predicate(i)) {
itemsToKeep.Add(i);
}
}
itemList = itemsToKeep;
答案 2 :(得分:6)
一些事情:
我不明白为什么你在删除项目时向前循环。您应该向后循环,并在执行删除时避免混乱的索引操作。
根据属性删除一个项目与另一个项目的关系是不正确的。您应该将其视为过滤列表。为此,您应该有一个方法需要Predicate<T>
,然后返回IEnumerable<T>
然后您可以枚举(或IList<T>
,如果您想要变异列表)。这样,您就可以对列表进行过滤和条件分离(这是更好的分离IMO)。
如果您可以访问LINQ,那么实际上没有理由这样做。您应该在原始列表中使用where过滤器,如果需要,将过滤逻辑分解为单独的方法,这些方法将获取该项并查看是否应该返回该项。然后,您可以构建where子句(或Predicate<T>
以传递给Where扩展方法)。
答案 3 :(得分:2)
这可能是最简单的方法,使用循环:
//counting backwards is easier when you are doing removals from a list
for( int i = lst.Count -1; i>= 0; i--)
{
if(condition1 || condition2)
{
RemoveFromDB(lst[i]);
lst.RemoveAt(i);
}
}
你可以重构它以使用框架提供的功能方法:
var toRemove = lst.FindAll(
item => !PricingOptionIsValid(item) || !item.ItemIsOnSite()
);
toRemove.ForEach( item =>
{
RemoveFromDB(item.ID);
lst.Remove(item);
});
你可以在没有toRemove变量的情况下编写它,将ForEach链接到FindAll上
答案 4 :(得分:2)
关于如何简化和澄清当前代码,有几个很好的建议。
在考虑性能总是从干净简洁的代码开始,在您确认需要优化之前不要担心优化。
几乎所有人都用高级语言写作:
a)可以更快。
b)快速充足。
答案 5 :(得分:1)
似乎标准方法,如果可能的话,是从循环内容中创建一个函数,以及执行这两个任务的函数:
doBothThings()
{
for(sharedLoop)
{
function1(loop counter);
function2(loop counter);
}
}
为您提供性能,但仍然将两个功能分成不同的功能。显然,如果两个函数在循环之前/之后涉及代码,那就不那么简单了。
答案 6 :(得分:1)
在C#中,我几乎肯定不会重复删除逻辑。考虑类似的事情:
private delegate bool RemovalExpression(ItemType item);
private void RemoveItems(RemovalExpression shouldRemove)
{
for (int s = 0; s < itemList.Count; s++)
{
if (shouldRemove(itemList[s]))
{
RemoveItem(itemList[s].Id);
itemList.RemoveAt(s);
s--;
}
}
}
可以自然地用作:
RemoveItems(item => !item.ItemIsOnSite);
RemoveItems(item => !PricingOptionIsValid(item));
RemoveItems(item => (!item.ItemIsOnSite || !PricingOptionIsValid(item)));
等
同样从它的声音来看,你不应该担心在这一点上循环微优化。如果您没有明确表示您在项目移除中花费不成比例的时间的数据,则无法知道哪个构造将“更快”,也无法知道您的选择是否值得时间投入,或不必要的并发症,或对业绩的彻底悲观化。
因此,为了可维护性而写;简单明了,不重复逻辑。
答案 7 :(得分:1)
由于您的两种方法都是从列表中删除项目,因此组合循环并不一定有意义。您应该确定您使用的两种方法之间是否存在显着的性能差异。
例如,如果PricingOptionIsValid
是一项昂贵的操作(命中数据库或其他任何操作),那么在第一次循环中修剪尽可能多的项目后,您可能希望在第二次循环中调用它。
如果测试的顺序无关紧要,则应首先放置更可能的分支,然后将分支放在第二位。如果ItemIsOnSite
仅在1%的时间内为假,那么您将花费大量的周期来跳过它。
您也可以考虑使用迭代器而不是操纵循环变量。无论是那个或只是在循环中找到要删除的项目,然后再做一个循环来删除它们。
答案 8 :(得分:1)
使用LINQ。而且,我们不是超过15分钟限制吗?
答案 9 :(得分:1)
如果我们正在讨论适当的OO,那么保留itemList和调用者同步的RemoveItem的责任可能不是一个好主意。
我个人会使用一个可以挂钩的OnRemove事件列表,并添加RemoveItem作为事件。这使得调用者无需记住调用RemoveItem。
这使代码更简单,允许您使用类似:
private void RemoveDiscontinuedItems()
{
itemList.RemoveAll(x => !x.ItemIsOnSite);
}
private void RemovePriceChangedItems()
{
itemList.RemoveAll(x => !PricingOptionIsValid(x));
}
代码更清晰,逻辑和目的更明显。
性能显然不必担心,除非它成为一个问题,但你必须记住用极值测试(在这种情况下是一个大的列表)。
如果您发现多次遍历列表实际上是一个瓶颈,那么我建议这样的事情:
private bool IsDiscontinuedItem(Item item)
{
return !item.ItemIsOnSite;
}
private bool IsPriceChangedItem(Item item)
{
return !PricingOptionIsValid(item);
}
private bool IsInvalidItem(Item item)
{
return IsDiscontinuedItem(item) ||
IsPriceChangedItem(item);
}
private void RemoveInvalidItems()
{
itemList.RemoveAll(IsInvalidItem)
}
答案 10 :(得分:1)
做任何有意义的事情,降低复杂性,并且最容易维护。最佳实践是指南。
答案 11 :(得分:0)
这是我的版本:
private void RemoveDiscontinuedItems()
{
RemoveItems(item => item.ItemIsOnSite);
}
private void RemovePriceChangedItems()
{
RemoveItems(item => PricingOptionIsValid(item));
}
private void RemoveAll()
{
RemoveItems(item => item.ItemIsOnSite || PricingOptionIsValid(item));
}
private void RemoveItems(Predicate<Item> removeIfTruePredicate)
{
itemList.RemoveAll(
item =>
{
if(removeIfTruePredicate(item))
{
RemoveItem(item);
return true;
}
return false;
});
}
请注意,这是基于您对可变列表的使用。我很想考虑是否真的需要一个可变列表。如果没有,我宁愿使用更多功能的样式,并使用linq where子句创建一个新列表,排除需要删除的项目。
考虑到你对性能的渴望,我的猜测是RemoveAll(我假设你正在使用List<T>
)比创建新列表更快。当然,你真的需要检查一下这一点。