【问题标题】:Split an Iterator containing two foreach loops in two different functions在两个不同的函数中拆分包含两个 foreach 循环的迭代器
【发布时间】:2021-12-18 05:26:10
【问题描述】:

我有当前的功能:

private IEnumerable<string> GetKeysStringValues(RegistryKey key)
{
    foreach (var subKey in GetAllSubKeys(key))
    {
        foreach (var value in subKey.GetValueNames())
        {
            if (subKey.GetValueKind(value) == RegistryValueKind.String)
            {
                yield return (string) subKey.GetValue(value);
            }
        }
    }
}

它:

  1. 解析注册表项的所有子项
  2. 对于每个子键,解析其所有值
  3. 如果值为字符串,则将其添加到迭代器中

我担心的是有两个嵌入式 for-each 循环,我一点也不喜欢。 我想把这个函数一分为二。

问题是我最终的返回类型为IEnumerable&lt;IEnumerable&lt;string&gt;&gt;

我尝试只在第二个函数中构建迭代器,并在第一个函数中直接返回它,但是这样做我错过了所有后续调用。

这是导致它的代码:

    private IEnumerable<IEnumerable<string>> GetSubKeysStringValues(RegistryKey key)
    {
        IEnumerable<string> enumerable = Enumerable.Empty<string>();
        
        foreach (var subKey in GetAllSubKeys(key))
        {
            yield return GetKeysStringValues(subKey));
        }
    }

    private IEnumerable<string> GetKeysStringValues(RegistryKey key)
    {
        foreach (var value in key.GetValueNames())
        {
            if (key.GetValueKind(value) == RegistryValueKind.String)
            {
                yield return (string) key.GetValue(value);
            }
        }
    }

你会怎么做?

编辑:

到目前为止,我有这个解决方案,但可以改进吗?

private IEnumerable<string> GetSubKeysStringValues(RegistryKey key)
{
    IEnumerable<string> enumerable = Enumerable.Empty<string>();
    
    foreach (var subKey in GetAllSubKeys(key))
    {
        enumerable = enumerable.Concat(GetKeysStringValues(subKey));
    }

    return enumerable;
}

private IEnumerable<string> GetKeysStringValues(RegistryKey key)
{
    foreach (var value in key.GetValueNames())
    {
        if (key.GetValueKind(value) == RegistryValueKind.String)
        {
            yield return (string) key.GetValue(value);
        }
    }
}

【问题讨论】:

  • 两个foreach有什么问题?您还应该说明导致IEnumerable&lt;IEnumerable&lt;string&gt;&gt; 的原因。
  • 两个 foreach 很难进行单元测试,在这种情况下不尊重 SRP
  • 我按照你的要求上传了代码

标签: c# iterator generator yield-return


【解决方案1】:

到目前为止,我有这个解决方案,但可以改进吗?

您的解决方案很好。如果您想要更紧凑的代码,可以使用 LINQ 方法SelectMany,其目的是将IEnumerable&lt;IEnumerable&lt;T&gt;&gt;“扁平化”为IEnumerable&lt;T&gt;

private IEnumerable<string> GetSubKeysStringValues(RegistryKey key)
{
    return GetAllSubKeys(key).SelectMany(subKey => GetKeysStringValues(subKey));
}

...或者,如果您更喜欢查询语法(产生相同的结果,甚至可能是相同的 IL 代码):

private IEnumerable<string> GetSubKeysStringValues(RegistryKey key)
{
    return from subKey in GetAllSubKeys(key)
           from value in GetKeysStringValues(subKey)
           select value;
}

【讨论】:

  • 我喜欢那个!现在,我想对每个返回的结果调用一个函数,并通过 LINQ 查询来完成。到目前为止它不起作用,我只能使用 foreach 循环来做到这一点
  • @Zangdar:你的意思是像from x in returnedResults select myFunction(x)这样的东西吗?
  • 是的。执行此操作时,出现此错误:无法从中推断方法 'IEnumerable System.Linq.Enumerable.Select(this IEnumerable, Func)' 的类型参数用法。尝试明确指定类型参数。
  • @Zangdar:myFunction 是否返回值?如果不是,那真的不适合 LINQ,因为 linq 是一种查询语言。如果是,我建议您使用minimal reproducible example 创建一个新问题。
  • 它没有,它是一个提供复制服务的功能。我习惯了 Python 理解,但我想在这种情况下没有等价物,我应该依赖 foreach 循环
猜你喜欢
  • 2014-06-30
  • 2014-10-23
  • 2016-05-08
  • 2016-04-07
  • 2019-03-26
  • 1970-01-01
  • 2020-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多