c# - 有什么方法可以将此查询重写为更多 "LINQ-ier"吗?

标签 c# linq

下面将一个 / 分隔的 URL 路径解析成键值对的字典:

    private Dictionary<string, string> ParsePathParameters(string path)
    {
        var parameters = new Dictionary<string, string>();
        if (string.IsNullOrEmpty(path))
        {
            return parameters;
        }

        var pathSegments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

        for (var i = pathSegments.Length - 1; i >= pathSegments.Length % 2; i -= 2)
        {
            parameters.Add(pathSegments[i - 1], pathSegments[i]);
        }

        return parameters;
    }

输入格式是[/preamble][/key1/value1][/key2/value2]...[/keyN/valueN] 例如,给定输入“/foo/1/bar/Thing"或 "/slug/foo/1/bar/Thing",输出将是:

Dictionary<string, string>
{
    { "foo", "1" },
    { "bar", "Thing" },
}

这段代码很好;简单,不言自明,快速。但是,因为我喜欢挑战,所以我决定用 LINQ 重写它:

    private Dictionary<string, string> ParsePathParameters(string path)
    {
        if (string.IsNullOrEmpty(path))
        {
            return new Dictionary<string, string>();
        }

        var pathSegments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
        var skip = pathSegments.Length % 2;

        return pathSegments.Skip(skip)
            .Where((_, i) => i % 2 == 0)
            .Select((_, i) => i * 2)
            .ToDictionary(i => pathSegments[i + skip], i => pathSegments[i + skip + 1]);
    }

这行得通,但绝对不是最佳选择,可能是因为它也不是使用 LINQ 实现此目的的“正确”方式。谁能建议是否有可能以更“类似 LINQ”的方式编写此代码,如果可以,请给我一些这方面的建议?

最佳答案

我会这样写:

private Dictionary<string, string> ParsePathParameters(string path)
{
    return GetSegmentPairs().ToDictionary(x => x.k, x => x.v);
    IEnumerable<(string k, string v)> GetSegmentPairs()
    {
        var segments = path?.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
                ?? new string[0];
        for (int i = 0, l = segments.Length; i < l; i += 2)
            yield return (segments[i+0], segments[i+1]);
    }
}

不要低估局部函数和生成器的力量。当您需要创建难以作为直接 linq 查询编写的序列时,生成器非常有用。然后可以在 linq 查询中使用这些生成器。对于这种特殊情况,对于相当简单的查询甚至可能没有必要,但对于更复杂的查询,它是无价的。但这是一种您应该学会更频繁地使用的模式。

如果使用 C# 8,我会养成在适当的地方使用跨度/内存和切片的习惯。

private Dictionary<string, string> ParsePathParameters(string path)
{
    return GetSegments().ToDictionary(x => x.Span[0], x => x.Span[1]);
    IEnumerable<System.Memory<string>> GetSegments()
    {
        var segments = path?.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
        for (int i = 0, l = segments.Length; i < l; i += 2)
            yield return segments[^i..i+1];
    }
}

否则,如果您使用的是 MoreLINQ,则可以使用 Pairwise()TakeEvery() 方法来有效地执行与 GetSegmentPairs 相同的操作() 上面的方法。

private Dictionary<string, string> ParsePathParameters(string path) =>
    (path?.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
            ?? Enumerable.Empty<string>())
        .Pairwise(ValueTuple.Create)
        .TakeEvery(2) // pairwise produces overlapping pairs so take every other
        .ToDictionary(x => x[0], x => x[1]);

关于c# - 有什么方法可以将此查询重写为更多 "LINQ-ier"吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54977221/

相关文章:

c# - 创建一个可以使用 stdcall 在 Delphi 应用程序中导入的 C# DLL - 可能吗?

c# - 如何将文本放在 RadioButton 的顶部

c# - 调用不同的 AJAX 帖子后,MVC AJAX 表单不会提交

vb.net - 使用 linq vb.net 进行分组和计数

c# - EF linq模拟中临时创建的表中的异常

c# - 使用 lambda 为通用列表中的每个对象执行方法

c# - 异步打开读取

c# - PHP 和 C# 中的 DES 加密

c# - SelectMany 时最小起订量验证失败

linq - 使用 LINQ SUM 时的异常