c# - 使用 LINQ 更新字符串数组

标签 c# .net linq linq-to-objects

我正在尝试创建一种方法,使用 LINQ 使用新版本字符串更新 AssemblyInfo 文件。我可以成功提取需要更新的字符串,但不确定如何更新集合中的项目。我是 LINQ 的新手,非常感谢任何建议!

private void UpdateVersion(string file, string version)
    {
        string[] asmInfo = File.ReadAllLines(file);
        var line = asmInfo.Single(x => x.Trim().StartsWith("[assembly: AssemblyVersion"));
        line = "[assembly: AssemblyVersion\"" + version + "\")]";

        // This doesn't work - it's writing the original file back
        File.WriteAllLines(file, asmInfo);
    }

最佳答案

I'm new to LINQ, so any advice is appreciated!

Linq 主要围绕查询、聚合和过滤数据而设计,而不是围绕直接修改现有数据而设计。您可以使用它来制作数据的修改副本,并用该副本覆盖原始数据。

不过,您遇到的问题并不完全是因为 Linq。

让我们检查一下您的代码:

string[] asmInfo = File.ReadAllLines(file);

您正在将文件的所有行复制到内存中的字符串数组中。

var line = asmInfo.Single(x => x.Trim().StartsWith("[assembly: AssemblyVersion"));

您正在查询所有行,并将行(或多或少按值)复制到变量 line 中.

line = "[assembly: AssemblyVersion\"" + version + "\")]";

您正在用不同的内容覆盖复制的行。这不会修改原始数组。

File.WriteAllLines(file, asmInfo);

您正在将原始数组写回文件。您没有更改数组,因此您不会在文件中看到任何更改。

解决这个问题的几种方法:

  • 像您目前所做的那样复制数组,找到要更改的行的索引,然后修改数组(按索引)。此选项根本不使用 Linq。
  • 使用 linq 制作数据的转换副本,并将整个转换后的副本写回。此选项使用 linq,但不利用其功能。
  • 停止使用 File.ReadAllLines/File.WriteAllLines , 并一次读/写一行。此选项实际上利用了 Linq 的强大功能,但最为复杂。

修改数组

此方法根本不使用 Linq:

string[] asmInfo = File.ReadAllLines(file);
int lineIndex = Array.FindIndex(asmInfo,
    x => x.Trim().StartsWith("[assembly: AssemblyVersion"));
asmInfo[lineIndex] = "[assembly: AssemblyVersion\"" + version + "\")]";
File.WriteAllLines(file, asmInfo);

假设您的文件很小,这是完成任务的一种相当标准的方法。

查询并制作数据的转换副本,并写出副本

这个方法更像 Linq:

string[] asmInfo = File.ReadAllLines(file);
var transformedLines = asmInfo.Select(
    x => x.Trim().StartsWith("[assembly: AssemblyVersion")
        ? "[assembly: AssemblyVersion\"" + version + "\")]"
        : x
    );
File.WriteAllLines(file, asmInfo.ToArray());

这比前面的例子更像 Linq。但它实际上效率较低,因为 File.WriteAllLines不能接受 IEnumerable<string> .因此,您被迫调用 ToArray .当您调用 ToArray (或 ToList ),整个查询被运行并复制到一个新数组中,因此您有两个文件副本。

如果File.WriteAllLines花了一个IEnumerable<string> ,那么您就不必制作额外的副本,并且每一行都将在查询仍在运行时写入。

一次读写一行

这个选项是最有效的,实际上显示了 Linq 的一些好处 - 更具体地说,IEnumerable ,这是 Linq 大部分功能的来源。

public static class FileStreamExtensions
{
    public static IEnumerable<string> GetLines(this FileStream stream)
    {
        using (var reader = new StreamReader(stream))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
                yield return line;
        }
    }

    public static void WriteLines(this FileStream stream, IEnumerable<string> lines)
    {
        using (var writer = new StreamWriter(stream))
        {
            foreach (string line in lines)
            {
                writer.WriteLine(line);
            }
        }
    }
}

private void UpdateVersion(string file, string version)
{
    using (FileStream inputFile = File.OpenRead(file))
    {
        string tempFilePath = Path.GetTempFileName();

        var transformedLines = inputFile.GetLines().Select(
            x => x.Trim().StartsWith("[assembly: AssemblyVersion")
                ? "[assembly: AssemblyVersion\"" + version + "\")]"
                : x
            );

        using (FileStream outputFile = File.OpenWrite(tempFilePath))
        {
            outputFile.WriteLines(transformedLines);
        }

        string backupFilename = Path.Combine(Path.GetDirectoryName(file), Path.GetRandomFileName());
        File.Replace(tempFilePath, file, backupFilename);
    }
}

这需要更多的代码,因为:

  • 数据的中间副本现在位于临时文件中,而不是内存中的数组。
  • 流类本身不提供逐行枚举器,因此我们自己构建了它们。

如果您在调试器中运行它并在 yield return line 上设置断点,您会看到一些有趣的事情。和 writer.WriteLine声明。读取代码和写入代码(或多或少)同时运行。首先读取一行,然后写入。

这是因为 IEnumerableyield return ,这是 Linq 不仅仅是语法糖的主要原因之一。

关于c# - 使用 LINQ 更新字符串数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6394165/

相关文章:

c# - 如何禁用下载提示,如 'do you want to open or save or save_as?'

c# - 无法满足安全 token 请求,因为关闭 TLS 1.0 时身份验证失败

c# - 多个 LINQ 表达式和动态属性

c# - 处理linq组的方法

c# - 使用 sql 数据读取器循环遍历 sql 表中的所有值

c# - 如何知道用户在网站上是否仍然在线或离线?

c# - Gecko 45 c# winform 添加脚本到文档

.net - VS 2015 中未解决 Microsoft.NETCORE.app 依赖关系

c# - 有没有办法为泛型实例提供构造函数参数?

c# - 使用 LINQ 过滤数据集中的数据并将结果作为字符串列表