【问题标题】:recursive function fails to get the expected result in c#递归函数无法在c#中得到预期的结果
【发布时间】:2019-07-09 10:25:36
【问题描述】:

我有以下课程,

用户:

public class User:Domain
    {
        [Sortable]
        public string FirstName { get; set; }

        [Sortable]
        public string LastName { get; set; }

        [NestedSortable]
        public Profile Profile { get; set; }
    }

简介:

public class Profile : Domain
    {
        [Sortable]
        public string BrandName { get; set; }

        [NestedSortable]
        public Client Client { get; set; }

        [NestedSortable]
        public ProfileContact ProfileContact { get; set; }
    }

客户:

public class Client : Domain
    {
        [Sortable]
        public string Name { get; set; }
    }

个人资料联系人:

public class ProfileContact : Domain
    {
        [Sortable]
        public string Email { get; set; }
    }

我正在编写一个递归函数来获取所有用[Sortable] 属性修饰的属性。这在我有一个 [NestedSortableProperty] 时效果很好,但在我有多个时会失败。

这是我的递归函数:

private static IEnumerable<SortTerm> GetTermsFromModel(
            Type parentSortClass,
            List<SortTerm> sortTerms,
            string parentsName = null,
            bool hasNavigation = false)
        {
            if (sortTerms is null)
            {
                sortTerms = new List<SortTerm>();
            }

            sortTerms.AddRange(parentSortClass.GetTypeInfo()
                       .DeclaredProperties
                       .Where(p => p.GetCustomAttributes<SortableAttribute>().Any())
                       .Select(p => new SortTerm
                       {
                           ParentName = parentSortClass.Name,
                           Name = hasNavigation ? $"{parentsName}.{p.Name}" : p.Name,
                           EntityName = p.GetCustomAttribute<SortableAttribute>().EntityProperty,
                           Default = p.GetCustomAttribute<SortableAttribute>().Default,
                           HasNavigation = hasNavigation
                       }));

            var complexSortProperties = parentSortClass.GetTypeInfo()
                       .DeclaredProperties
                       .Where(p => p.GetCustomAttributes<NestedSortableAttribute>().Any());

            if (complexSortProperties.Any())
            {
                foreach (var parentProperty in complexSortProperties)
                {
                    var parentType = parentProperty.PropertyType;

                    if (string.IsNullOrWhiteSpace(parentsName))
                    {
                        parentsName = parentType.Name;
                    }
                    else
                    {
                        parentsName += $".{parentType.Name}";
                    }

                    return GetTermsFromModel(parentType, sortTerms, parentsName, true);
                }
            }

            return sortTerms;
        }

这是因为 foreach 循环中的 return 语句。如何重写这个?在这个例子中,我需要得到FirstNameLastNameBrandNameNameEmail 的列表。但除了Email,我只得到前四个属性。

现在,上述问题已通过删除我在下面我自己的答案中发布的返回语句并遵循@Dialectus cmets 使用yield return 来解决。所以我罢工并更新了问题。

现在我遇到了另一个问题。如果一个类有多个[NestedSortable] 属性,则错误地分配了父类名称。

这个方法第一次被User类调用,比如var declaredTerms = GetTermsFromModel(typeof(User), null);

例子,

第一次调用后,parentsName 参数将为空,User 类中的[Sortable] 属性没有任何作用。

现在对于User 类中的[NestedSortable] Profile 属性,parentsName 将是Profile,因此Profile 类中的[Sortable] 属性将具有Name 作为Profile.BrandName 和以此类推。

Name最终列表中的属性如下,

预期输出:

名字、姓氏、Profile.BrandName、Profile.Client.Name、Profile.ProfileContact.Email

但实际输出:

名字、姓氏、Profile.BrandName、Profile.Client.Name、Profile.Client.ProfileContact.Email

请协助解决此问题。

谢谢,

阿卜杜勒

【问题讨论】:

  • sortTerms 变量不应该是方法的输入参数。它实际上应该是方法的输出。此外,该方法返回IEnumerable,但任何地方都没有yield return。请参阅此以了解如何从递归函数yield returnstackoverflow.com/q/2055927/395718
  • @Dialectus 什么是产量以及为什么需要进行此更改?请帮忙
  • 当需要学习新东西时,我更容易将您重定向到谷歌。搜索 c# yield return。这就是IEnumerable 的工作方式。
  • 为什么不应该输入sortTerms
  • 应该是foreach (var x in ...) yield return x;,而不是sortTerms.AddRange(...)。并且递归调用GetTermsFromModel也应该在循环中,foreach (var x in GetTermsFromModel(...)) yield return x;

标签: c# recursion


【解决方案1】:

经过一些调试,我删除了 foreach 循环中的 return 语句,从而解决了第一个问题。

改变了,

return GetTermsFromModel(parentType, sortTerms, parentsName, true);

到,

GetTermsFromModel(parentType, sortTerms, parentsName, true);

然后根据@Dialecticus cmets 删除传递sortTerms 作为输入参数并删除代码中的参数并将sortTerms.AddRange(...) 更改为yield return

改变了,

sortTerms.AddRange(parentSortClass.GetTypeInfo()
                       .DeclaredProperties
                       .Where(p => p.GetCustomAttributes<SortableAttribute>().Any())
                       .Select(p => new SortTerm
                       {
                           ParentName = parentSortClass.Name,
                           Name = hasNavigation ? $"{parentsName}.{p.Name}" : p.Name,
                           EntityName = p.GetCustomAttribute<SortableAttribute>().EntityProperty,
                           Default = p.GetCustomAttribute<SortableAttribute>().Default,
                           HasNavigation = hasNavigation
                       }));

到,

foreach (var p in properties)
            {
                yield return new SortTerm
                {
                    ParentName = parentSortClass.Name,
                    Name = hasNavigation ? $"{parentsName}.{p.Name}" : p.Name,
                    EntityName = p.GetCustomAttribute<SortableAttribute>().EntityProperty,
                    Default = p.GetCustomAttribute<SortableAttribute>().Default,
                    HasNavigation = hasNavigation
                };
            }

也适用于复杂的属性,变化自,

GetTermsFromModel(parentType, sortTerms, parentsName, true);

到,

var complexProperties = GetTermsFromModel(parentType, parentsName, true);

                    foreach (var complexProperty in complexProperties)
                    {
                        yield return complexProperty;
                    }

对于我面临的最后一个问题,在内部foreach 循环修复它之后添加以下代码,

parentsName = parentsName.Replace($".{parentType.Name}", string.Empty);

所以这里是完整更新的工作代码:

private static IEnumerable<SortTerm> GetTermsFromModel(
            Type parentSortClass,
            string parentsName = null,
            bool hasNavigation = false)
        {
            var properties = parentSortClass.GetTypeInfo()
                       .DeclaredProperties
                       .Where(p => p.GetCustomAttributes<SortableAttribute>().Any());

            foreach (var p in properties)
            {
                yield return new SortTerm
                {
                    ParentName = parentSortClass.Name,
                    Name = hasNavigation ? $"{parentsName}.{p.Name}" : p.Name,
                    EntityName = p.GetCustomAttribute<SortableAttribute>().EntityProperty,
                    Default = p.GetCustomAttribute<SortableAttribute>().Default,
                    HasNavigation = hasNavigation
                };
            }

            var complexSortProperties = parentSortClass.GetTypeInfo()
                       .DeclaredProperties
                       .Where(p => p.GetCustomAttributes<NestedSortableAttribute>().Any());

            if (complexSortProperties.Any())
            {
                foreach (var parentProperty in complexSortProperties)
                {
                    var parentType = parentProperty.PropertyType;

                    //if (string.IsNullOrWhiteSpace(parentsName))
                    //{
                    //    parentsName = parentType.Name;
                    //}
                    //else
                    //{
                    //    parentsName += $".{parentType.Name}";
                    //}

                    var complexProperties = GetTermsFromModel(parentType, string.IsNullOrWhiteSpace(parentsName) ? parentType.Name : $"{parentsName}.{parentType.Name}", true);

                    foreach (var complexProperty in complexProperties)
                    {
                        yield return complexProperty;
                    }

                    //parentsName = parentsName.Replace($".{parentType.Name}", string.Empty);
                }
            }
        }

【讨论】:

  • 存在一个潜在的错误,即最后一个 Replace 将删除的不仅仅是最后一个 parentType.Name(如果字符串中有多个这样的标记子字符串)。我建议您不要操纵parentsName,而是将附加的字符串直接传递给递归调用GetTermsFromModel(parentType, parentsName + $".{parentType.Name}", true)
  • 好收获。但是如果我第一次直接通过,那么当它为空时,它将是.User,这会产生意想不到的输出,对吧?
  • 对,你可以在这种情况下使用条件运算符string.IsNullOrWhiteSpace(parentsName) ? parentType.Name : $"{parentsName}.{parentType.Name}"
  • 根据您的 cmets 更新了答案。非常感谢您的指导。
猜你喜欢
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 2021-09-01
  • 2022-07-06
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
相关资源
最近更新 更多