2017-03-03 38 views
1

我有一个类如下:递归列表中包含财产

public class Feature 
{ 
    public string Name { get; set; } 

    public string DisplayName { get; set; } 

    public List<Feature> SubFeatures { get; set; } = new List<Feature>(); 
} 

然后我有一个List<Feature> features = new List<Feature>;在那里我存储我的所有功能。

现在,我想知道在我的features变量中是否存在特定Feature(按名称)。

然而,它可以存在于例如SubFeatureSubFeature的任何级别(SubFeature)。

我已经得到的最接近的是这样的:

public bool FeatureExists(Feature feature, string name) 
{ 
    return feature.Name == name || feature.SubFeatures.Select(subFeature => FeatureExists(subFeature, name)).Any(result => result); 
} 

但它涉及到有使用上的FeatureExists()呼叫者在同一时间给它传递一个顶级功能。

我确定有一个更简单的方法来做到这一点,我该如何正确地做到这一点?

+0

为什么你需要使用'for'?你不能指向所有功能的顶级父母吗?这个方法被递归地调用,所以它将针对每个“SubFeatures”对象执行。 – foobar

+0

你可以做的是创建一个名为层次结构的新变量.......从你的程序可以找出多少时间递归列表子功能:) –

+3

我觉得我误解了一些东西......你是基本上要求'features.Any(x => FeatureExists(x,featureName))'? – grek40

回答

3

定义一个递归方法是这样的:

public IEnumerable<Feature> FeatureAndSubFeatures(Feature feature) 
{ 
    yield return feature; 

    foreach (var subFeature in feature.SubFeatures) 
    { 
     foreach (var child in FeatureAndSubFeatures(subFeature)) 
     { 
      yield return child; 
     } 
    } 
} 

然后使用它:

FeatureAndSubFeatures(feature).Any(x => x.Name == name); 

另一种选择是把这个方法在功能本身,所谓的像SelfAndSubFeaturesRecursive()。

这种方法 - 编写一个递归地扁平化树的方法,而不是编写一个特定方法来搜索具有给定名称的特征 - 是非常灵活的,因为您可以使用它来搜索任何基于树的节点标准或节点的任何子集,而不是专门用于查找具有特定名称的节点。

您也可以编写它以开始使用一组功能。喜欢的东西:

public IEnumerable<Feature> FeaturesAndSubFeatures(IEnumerable<Feature> features) 
{ 
    foreach (var feature in features) 
    { 
     yield return feature; 

     foreach (var child in FeaturesAndSubFeatures(feature.SubFeatures)) 
     { 
      yield return child; 
     } 
    } 
} 

这仅仅是有用的,如果你总是有特征集合出发,但你做的情况下节省了SelectMany

+0

好吧,这与我所得到的非常相似,但是我仍然存在的问题是我从一个'List '开始,并且该方法接受单个特征。我如何搜索所有这些? – cogumel0

+0

你可以写'features.SelectMany(x => FeatureAndSubFeatures(x))。任何(...)',或者你可以重写'FeatureAndSubFeatures'来获得一个'IEnumerable '。 – canton7

+0

虽然在这里有很多正确的答案(正如在评论中指出的那样,最简单的事情就是对于我来说只是做'features.Any(x => FeatureExists(x,“无论”))“,这种方法很远更灵活 – cogumel0

0

你想为你做一个单独的递归方法。 试试这个:

public bool FeatureExists(Feature feature, string name) { 
    if(feature.Name == name) { 
     return true; 
    } 
    if(!feature.SubFeatures.isEmpty) { 
     foreach(Feature subFeature in feature.SubFeatures){ 
      FeatureExists(subFeature, name) 
     } 
    } 
     return false; 
    } 
1

我尽量避免递归。这是一个版本,而不:

public bool FeatureExists(Feature feature, string name) 
{ 
    var featureQueue = new Queue<Feature>(); 
    featureQueue.Enqueue(feature); 
    while (featureQueue.Count > 0) 
    { 
     Feature current = featureQueue.Dequeue(); 
     if (feature.Name == name) 
      return true; 
     foreach (Feature f in current.SubFeatures) 
      featureQueue.Enqueue(f); 
    } 
    return false; 
} 

如果您发现本作评论,你可以使用一个通用的扩展方法,只要你需要递归检查,例如使用它的可读性:

public static class Extensions 
{ 
    public static bool RecursiveCheck<T>(this T rootItem, Func<T, IEnumerable<T>> getChildrenFunc, Func<T, bool> predicate) 
    { 
     var queue = new Queue<T>(); 
     queue.Enqueue(rootItem); 
     while (queue.Count > 0) 
     { 
      T current = queue.Dequeue(); 
      if (predicate(current)) 
       return true; 
      foreach (T child in getChildrenFunc(current)) 
       queue.Enqueue(child); 
     } 
     return false; 
    } 
} 

测试功能:

Feature f1 = new Feature 
{ 
    Name = "1", SubFeatures = new List<Feature> { new Feature {Name="1.1", SubFeatures = new List<Feature> {new Feature {Name= "thename" } } }} 
}; 

这简单的一行仍然是:

bool containsName = f1.RecursiveCheck<Feature>(f => f.SubFeatures, f => f.Name == "thename"); 
+1

你能详细说明为什么你试图避免递归吗?我很想知道更多关于这个的信息。 – cogumel0

+1

@ cogumel0:递归通常更难以理解,更容易引起错误,它可能会导致'StackOverFlowExceptions '等 –

+1

但是我个人发现你的代码难以遵循,与当前的递归方法相反。StackOverflow'部分我同意。 – Emad

0

嗯,我不确定这是你问什么,但如果你想要更干净的代码,你可以写你的函数作为扩展方法,并使用LINQ而不是循环来获得更干净的代码。

public static bool FeatureExists(this Feature feature, string name) 
{ 
    return feature.Name == name || feature.SubFeatures.Select(subFeature => FeatureExists(subFeature, name)).Any(result => result); 
} 

然后

List<Feature> mainFeatures = new List<Feature>(); 
mainFeatures.Any(obj => obj.FeatureExists("abc")); 

如果你想事件更短,更干净的代码,你可以考虑有一个特征作为像母亲一样功能的所有功能,父母,然后打电话给你的递归方法在这一点。

但无论如何,考虑让你的方法扩展方法。

+0

您的代码可以简化为'mainFeatures.Any(obj => obj.FeatureExists(“abc”))''。但是,它很好地回答了这个问题。 – cogumel0

+0

@ cogumel0谢谢我试图回答很快错过了这个。 – Emad

0

以下应该通过所有列表重复并填写“结果”与包含在整个层级中的所有可能的特征

public class Feature 
    { 
     public string Name { get; set; } 

     public string DisplayName { get; set; } 

     public List<Feature> SubFeatures { get; set; } = new List<Feature>(); 
    } 

其他类

List<feature> result = new List<feature>(); 

public void FindItems(Feature yourFeature) 
{ 
    result.add(yourFeature); 
    foreach(Feature feature in yourFeature) 
    { 
     if(feature.SubFeatures.count != 0) 
     { 
     foreach(Feature subfeature in feature) 
     { 
      FindItems(subfeature); 
     } 
     } 
     else 
     { 
     result.add(feature); 
     } 
    } 
} 
+0

使用yield return生成'IEnumerable '效率更高,特别是如果在找到具有给定名称的第一个节点后搜索将停止。即使正在搜索的节点是第一个节点,您的方法也会使整个树变平。 – canton7

+0

扁平化整个树是他想要的,即时通讯只是想知道,如果你的递归如果层次结构是5-10深?将会发生什么?(合法的问题,由我可以遵循它的原因将只有约3深) –

+0

当然,但他不需要将整棵树包含在列表中。当他找到符合他的标准的项目时,他只需要搜索树并**停止**。将整棵树放在新列表中是浪费时间和空间。 – canton7