这可以重构成漂亮的LINQ吗?

时间:2008-10-25 16:49:38

标签: linq refactoring

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

5 个答案:

答案 0 :(得分:5)

我正在按照我的想法输入这个内容,因此它显示了一系列思路以及一个答案。

  • 您的来源只是cachedCrumbs
  • 你想添加设置了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!)

答案 1 :(得分:1)

首先,该代码不起作用。我会猜测你使用“碎屑”的一些地方你的意思是“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;
    }
}

答案 2 :(得分:1)

基于James Curran的替代答案 - 使用foreach声明肯定可以改进:

IList<Breadcrumb> crumbs = new List<BreadCrumb>();
foreach (Breadcrumb crumb in cachedCrumbs)
{
    crumbs.Add(crumb);
    if (crumb != null && crumb.IsCurrent)
    {
        break;
    }
}

答案 3 :(得分: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差异,但你应该明白这一点。

答案 4 :(得分: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的一个很棒的事情是实现同一目标的多种方式。