使用Linq计算父对象数量

4

我对使用linq表达式比较新,并尝试重构一些旧代码。有没有办法将以下方法转换为简短而干净的linq表达式?

public int GetParentCount(object o)
{ 
    int count = 0;
    object parent = GetParentObject(o);
    while (parent != null)
    {
        count++;
        parent = GetParentObject(parent);
    }
    return count;
}

我已经尝试搜索,但没有得到令人满意的结果。


4
你为什么想要使用LINQ来做这件事情? - MoonKnight
1
你能否发布涉及对象的结构?此外,GetParentObject实际上是做什么的 - 它如何获取父对象? - Floremin
4
我看不到任何递归... - Matthew Watson
@Killercam,我只是想知道是否有可能实现,如果是的话,如何实现。 - Mark
@Floremin 这是一种使用条件树结构进行排序的相当复杂的方法。 - Mark
@MatthewWatson 你说得对,我会修改标题。 - Mark
3个回答

2
您可以这样枚举祖先:
public IEnumerable<MyClass> AncestorsOf(MyClass obj)
{
   var parent = GetParentObject(obj);
   if (parent != null)
   { 
       yield return parent;
       foreach(var grandparent in AncestorsOf(parent))
          yield return grandparent;
   }
}

然后获取总数就很简单了,只需使用AncestorsOf(obj).Count()

3
FYI这并不是技术上的LINQ,它只是一个迭代器块。这是一个与LINQ很好协作的方法,但它本身不是LINQ。将其重构为递归方式也不太有帮助,它会使它变得更低效。如果您保持与OP使用相同的迭代方法,它可以更高效地完成相同的操作。 - Servy
正确的,唯一“是Linq”的部分是Count()。 Linq基本上只是在集合上进行操作,因此您需要创建一个IEnumerable / IQueryable对象。 我认为除非实现祖先的IEnumerable或递归地产生父项,否则无法将递归转换为可枚举对象。 也许我错了。 - Anders Forsgren

1
作为 Ander 解决方案的替代方案,可以采用非递归方法:
using System;
using System.Linq;
using System.Collections.Generic;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var obj = new object();
            int count = AllParents(obj).Count(); // Using Linq only here.
            Console.WriteLine(count);
        }

        public static IEnumerable<object> AllParents(object obj)
        {
            while (true)
            {
                obj = GetParentObject(obj);

                if (obj == null)
                    yield break;

                yield return obj;
            }
        }

        // This is merely a hacky test implementation.
        public static object GetParentObject(object obj)
        {
            if (--count == 0)
                return null;

            return obj;
        }

        private static int count = 10;
    }
}

0
这是一个通用函数,可以适用于任何类型的对象,并且可以使用包含父级的任何对象名称(使用Func<T,T>):
public static class MyExtensions {

  /// <summary>Gets an enumerable of all ancestors.</summary>
  public static IEnumerable<T> Ancestors<T>(this T obj, Func<T, T> expr) where T : class {
    obj = expr.Invoke(obj);
    while(obj != null) {
      yield return obj;
      obj = expr.Invoke(obj);
  }
}

这是一个使用该函数的示例应用程序:
class MyClass {
    public MyClass Parent { get; set; }
}

void Main()
{
    MyClass a = new MyClass();
    a.Parent = new MyClass();
    a.Parent.Parent = new MyClass();

    a.Ancestors(myObj => myObj.Parent).Count(); // Result: 2
}

如果您还要编写计算序列的方法,那么仅仅为了计数而创建一个方法是没有实际意义的,因为您总是可以在该方法上调用“Count”。这样做并没有增加价值。当然,这个答案也有与被接受的答案相同的问题。它实际上并没有使用LINQ来解决这个问题,而是创建了一个迭代器块。LINQ和迭代器块是两个完全不同的东西,尽管其中一个使用了另一个。 - Servy
@Servy,我最初只有AncestorCount,但为了支持Ancestor().Count(),我已经将其删除。LINQ不仅仅是查询数据集的一种方式吗?这不是一个可枚举的对象,而是一个对象,所以可能就是区别所在。我主要是把它放在这里作为之前答案的改进版本,希望它能提供一些价值。 - bradlis7
是的,LINQ 是一个用于查询数据集的功能集合,但问题非常具体,而且没有一个答案回答它。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接