Я новичок в 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);
Вы записываете исходный массив в файл.Вы не изменили массив, поэтому вы не увидите никаких изменений в файле.
Несколько способов решить эту проблему:
- Скопируйте массив, как выв настоящее время, найдите индекс строки, которую вы хотите изменить, и измените массив (по индексу).Эта опция вообще не использует 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
,Код чтения и код записи (более или менее) выполняются одновременно.Сначала читается строка, затем пишется.
Это из-за IEnumerable
и yield return
, и это одна из главных причин, почему Linq - это больше, чем просто синтаксический сахар.