1

我有一个 Breadcrumb 类型的 IList,它只是一个具有 NavigationTitle、NavigationUrl 和 IsCurrent 属性的轻量级类。它缓存在网络服务器上。我有一个方法可以构建当前的面包屑跟踪,直到第一个将 IsCurrent 设置为 true 的面包屑......使用下面的代码。它非常丑陋,绝对是一个快速的垃圾袋威利解决方案,但我很好奇,这可以很容易地重构为 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);
}
4

5 回答 5

5

我正在按我的想法输入这个,以便它显示一个思路以及一个答案。

  • 您的来源只是 cachedCrumbs
  • 您想添加第一个确实设置了 IsCurrent 的碎屑但之后什么都没有
  • 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!)

于 2008-10-25T17:08:18.813 回答
1

首先,该代码不起作用。我猜你使用“crumbs”的一些地方是指“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;
    }
}
于 2008-10-25T17:06:56.523 回答
1

基于 James Curran 的替代答案 - 这当然可以使用 foreach 语句来改进:

IList<Breadcrumb> crumbs = new List<BreadCrumb>();
foreach (Breadcrumb crumb in cachedCrumbs)
{
    crumbs.Add(crumb);
    if (crumb != null && crumb.IsCurrent)
    {
        break;
    }
}
于 2008-10-25T17:10:23.617 回答
1

怎么样...

// 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);

干净多了!

没有检查空值和非一案例以及 IList/IEnumerable 差异,但您应该明白这一点。

于 2008-10-25T17:45:29.317 回答
1

这个答案是 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 的美妙之处之一是实现同一目标的多种方式。

于 2008-10-25T19:19:45.383 回答