我有一个Breadcrumb类型的IList,它只是一个具有NavigationTitle、NavigationUrl和IsCurrent属性的轻量级类。它缓存在Web服务器上。我有一个方法,可以构建当前的面包屑跟踪,直到第一个将IsCurrent设置为true的面包屑。。。使用下面的代码。它非常丑陋,绝对是一个快速的dirtbag willie解决方案,但我很好奇,这能很容易地重构到LINQ中吗?
IList<Breadcrumb> crumbs = new List<Breadcrumb>();
bool foundCurrent = false;
for (int a = 0; a < cachedCrumbs.Count; a++)
{
crumbs.Add(crumbs[a]);
if (foundCurrent)
{
break;
}
foundCurrent = (crumbs[a + 1] != null && ((Breadcrumb)crumbs[a + 1]).IsCurrent);
}
我想怎么打就怎么打,这样它既显示了思路,也显示了答案。
- 你的来源只是缓存的隆隆声
- 您想要添加第一个crumb,确实设置了IsCurrent,但之后没有添加任何crumb
- TakeWhile听起来不错,但获得"以前的值有IsCurrent"有点痛苦
- 我们可以使用闭包来有效地保持一个变量,以确定最后一个值是否设置了IsCurrent
- 我们可以做一个有点"不操作"的选择,将TakeWhile与是否继续操作分开
因此,我们最终得到:
bool foundCurrent = false;
var crumbs = cachedCrumbs.TakeWhile(crumb => !foundCurrent)
.Select(crumb => {
foundCurrent = crumb == null || !crumb.IsCurrent;
return crumb; });
我还没有尝试过,但我认为它应该有效。。。不过可能有一个更简单的方法。
编辑:我认为在这种情况下,实际上一个直foreach循环更简单。话虽如此,您可以编写另一个类似TakeWhile的扩展方法,只是它也返回了导致条件失败的元素。那么它就简单到:
var crumbs = cachedCrumbs.NewMethod(crumb => crumb == null || !crumb.IsCurrent);
(我现在想不出一个合适的方法名称,因此NewMethod
!)
首先,该代码不起作用。我猜有些地方你用"面包屑"的意思是"cachedCrumbs"。如果是这样,代码可以简化为:
IList<Breadcrumb> crumbs = new List<Breadcrumb>();
for (int a = 0; a < cachedCrumbs.Count; a++)
{
crumbs.Add(cachedCrumbs[a]);
if (cachedCrumbs[a] != null && cachedCrumbs[a].IsCurrent)
{
break;
}
}
基于James Curran的替代答案-使用foreach语句肯定可以改进这一点:
IList<Breadcrumb> crumbs = new List<BreadCrumb>();
foreach (Breadcrumb crumb in cachedCrumbs)
{
crumbs.Add(crumb);
if (crumb != null && crumb.IsCurrent)
{
break;
}
}
怎么样。。。
// find the current item
var currentItem = cachedCrumbs.First(c => c.IsCurrent);
var currentIdx = cachedCrumbs.IndexOf(currentItem);
// get all items upto current item
var crumbs = cachedCrumbs.Take(currentIdx + 2);
您可以将其转换为TakeUpto方法,该方法将所有项都带到与您提供的谓词匹配的项。
怎么样:
public static IEnumerable<T> TakeUpto<T>(this IList<T> theList, Func<T, bool> predicate)
{
var targetItem = theList.First(predicate);
var targetIdx = theList.IndexOf(targetItem);
return theList.Take(targetIdx + 2);
}
然后你可以这样使用它:
var crumbs = cachedCrumbs.TakeUpto(c => c.IsCurrent);
干净多了!
虽然没有检查null和off by one case以及IList/IEnumerable差异,但您应该了解这个想法。
这个答案是chakrit的TakeUpTo:的替代实现
public static IEnumerable<T> TakeUpto<T>(this IEnumerable<T> theList, Func<T, bool> predicate)
{
foreach (T element in theList)
{
yield return element;
if (predicate(element))
{
break;
}
}
}
这只会在列表中迭代一次,这在各种情况下都可能是相关的。(假设上游序列是OrderBy子句的结果——你真的不希望它无缘无故地对结果进行多次排序。)
它还允许任何IEnumerable<T>
作为源,这使它更加灵活。
LINQ的美妙之处之一是可以通过多种方式实现相同的目标。