2010-05-13 4 views
3

У меня есть класс округа, который выглядит следующим образом:Как вернуть предков объекта с помощью LINQ?

public class District 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public District Parent { get; set; } 
    public IEnumerable<District> Ancestors { get { /* what goes here? */ } } 
} 

Я хотел бы, чтобы иметь возможность получить список предков каждого округа. Поэтому, если район «1.1.1» является ребенком района «1.1», который является ребенком района «1», получение предков в округе «1.1.1» вернет список, содержащий объекты округа, имена которых «1.1 »и« 1 ».

Это связано с утверждением возврата доходности (я никогда не понимал этого полностью)? Можно ли это сделать в одной строке?

ответ

9

Все может быть сделано в одной строке, если линия достаточно долго :)

В этом случае, вероятно, это Простейшим сделано не в одной строке:

public IEnumerable<District> Ancestors 
{ 
    get 
    { 
     District parent = Parent; 
     while (parent != null) 
     { 
      yield return parent; 
      parent = parent.Parent; 
     } 
    } 
} 

Просто краткий штекером если вы хотите лучше понять yield return - глава 6 первого выпуска C# в Depth по-прежнему доступна бесплатно, и это все об итераторах на C# 2. Возьмите его с first edition page.

+0

Это выглядит хорошо - попробуйте сейчас. – Chris

+0

как насчет циркулярной ссылки? –

+0

@Elijah: Я предполагаю, что там нет * круговых ссылок. Если есть, вам нужно сохранить набор и сделать что-то вроде: while (parent! = Null &&! VisitedParents.Contains (parent)) –

1

Возможно, вы захотите рассмотреть эту альтернативу ... ее не как «чистый», а массово эффективный способ легко запросить такие вещи. (Будь то с помощью SQL или нет)

SQL select descendants of a row

см «принято» ответ

единственное, что я хотел бы добавить это разделитель между тегами

ой, а затем запрос с использованием LINQ

ancestors = Districts.Where(d => 
     d.Pedigree.Contains(Id) && 
     d.Pedigree.Length < Pedigree) 
     .ToList(); 

для большинства, если вы используете ORM, это приведет к одному запросу, а не ко многим запросам при попытке итерации дерева

на самом деле в этом случае один из родителей, его еще проще, так как ваша родословная будет содержать свою родословную, но если у вас есть разветвление происхождения ..... :)

+0

Довольно проклятый умный. Отмеченный! – Chris

1

Вот общий метод расширения для получения сбор предков для объекта, на основании принятого ответа:

static public IEnumerable<T> GetAncestors<T>(this T source, Func<T, T> parentOf) 
{ 
    var Parent = parentOf(source); 
    while (Parent != null) 
    { 
     yield return Parent; 
     Parent = parentOf(Parent); 
    } 
} 

Так что я могу использовать его для всех иерархий в моем приложении:

public IEnumerable<District> Ancestors 
{ 
    get 
    { 
     return this.GetAncestors(d => d.Parent); 
    } 
} 

Или экс pand все родительские узлы данного узла TreeView ...

Node.GetAncestors(node => node.Parent).ToList().ForEach(n => n.Expanded = true); 

Это действительно удобно.