我正在尝试使用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);
}
答案 0 :(得分:6)
我是LINQ的新手,所以对任何建议表示赞赏!
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);
您正在将原始数组写回文件。您没有更改数组,因此您不会在文件中看到任何更改。
解决这个问题的几种方法:
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
语句中设置断点。读取代码和写入代码(或多或少)同时运行。首先读取一行,然后写入。
这是因为IEnumerable
和yield return
,这是Linq不仅仅是语法糖的主要原因之一。
答案 1 :(得分:3)
我建议使用Regex
:
private static void UpdateVersion(string file, string version)
{
var fileContent = File.ReadAllText(file);
var newFileContent = Regex.Replace(
fileContent,
@"(?<=AssemblyVersion\("")(?<VersionGroup>.*?)(?=""\))",
version);
File.WriteAllText(file, newFileContent);
}
你仍然可以使用LINQ来做这件事,但我认为这样效率会更低,而且更容易出错。
private static void UpdateVersion(string file, string version)
{
var linesList = File.ReadAllLines(file).ToList();
var line = linesList.Single(x => x.Trim().StartsWith("[assembly: AssemblyVersion"));
linesList.Remove(line);
linesList.Add("[assembly: AssemblyVersion(\"" + version + "\")]");
File.WriteAllLines(file, linesList.ToArray());
}
答案 2 :(得分:1)
您可能不应该尝试使用LINQ来修改原始查询源,因为它很笨拙且容易出错,而不是LINQ的“样式”。
相反,您可以使用LINQ作为过滤器来创建新数组。我们需要做的就是重新安排你的代码:
private void UpdateVersion(string file, string version)
{
string[] asmInfo = File.ReadAllLines(file);
asmInfo = asmInfo.Select(x => x.Trim().StartsWith("[assembly: AssemblyVersion") ?
"[assembly: AssemblyVersion\"" + version + "\")]" : x).ToArray();
File.WriteAllLines(file, asmInfo);
}