Вытащил из
http://www.rsdn.ru/forum/philosophy/3644575.1.aspxАвтор: Sinix
Дата: 18.12.09
Есть регулярная задачка "обойти структуру папок и сгенерить аналогичную структру, но с другими данными".
При этом
а) некоторые папки надо пропустить.
б) имена папок/файлов в папке назначения могут различаться и об этом надо знать вызывающему коду.
Как оно выглядит сейчас —
см код и пример ниже (убраны проверки, на практике лучше не использовать)
Реальный код — UniversIS.IO.FileSystemHelper.VisitAll() — брать
здесьАвтор: Sinix
Дата: 17.12.09
).
Страшно? Так вот, этот код с марта
1) существовал как один метод
2) был вынесен в библиотеку компонентов
3) параметры были объединены в структуру
4) был вынесен в отдельный класс, унаследованный от Component (раздулся втрое и стал вообще неподдерживаемым)
5) приведён к текущему виду и отмечен как // DONTTOUCH. Кстати, такие комментарии в Task List window — великое дело.
Придумаете как это написать красивее?
Злостный оффтоп
Сравнил время копирования (см код ещё ниже)
-кодом со всеми проверками,
-Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(src, dest2),
-Far'a (1,71a3, системные функции копирования)
-проводника win7.
Внимание вопрос:
Из какого места у товарищей растут руки, если мой абсолютно не оптимизированный код на паре тысяч мелких файлов выигрывает от %10 у кода из библиотеки VB и до 150% (30 сек vs 2 мин) у фара???
Не, понятно что без полноценной проверки на разных машинах/с разными наборами никаких выводов быть не должно. Но вопрос "чем оно там занимается" по-прежнему в силе.
Код для "переписать":
public delegate void DirectoryCallback(
string source, ref string destination,
ref bool newDestinationContainer, out bool skipSubitems);
public delegate void FileCallback(
string source, ref string destination,
bool newDestinationContainer);
public static void VisitAll(
string source, ref string destination,
DirectoryCallback directoryCallback, FileCallback fileCallback)
{
if (source.EndsWith("\\") || source.EndsWith("/"))
{
VisitAllInternal(
source, ref destination,
directoryCallback, fileCallback,
!Directory.Exists(Path.GetDirectoryName(destination)));
}
else
{
fileCallback(
source,
ref destination,
!Directory.Exists(Path.GetDirectoryName(destination.TrimEnd('\\','/'))));
}
}
private static void VisitAllInternal(
string source, ref string destination,
DirectoryCallback directoryCallback, FileCallback fileCallback, bool newDestinationContainer)
{
bool skip = false;
if (directoryCallback != null)
{
directoryCallback(source, ref destination, ref newDestinationContainer, out skip);
}
if (!skip)
{
if (fileCallback != null)
{
foreach (string fileName in Directory.GetFiles(source))
{
string destFile = destination + fileName.Substring(source.Length);
fileCallback(fileName, ref destFile, newDestinationContainer);
}
}
foreach (string dir in Directory.GetDirectories(source))
{
string sourceDir = dir + "\\";
string destDir = destination + sourceDir.Substring(source.Length);
VisitAllInternal(sourceDir, ref destDir, directoryCallback, fileCallback, newDestinationContainer);
}
}
}
Пример использования (хотя для такой простой задачи это оверкилл):
public static void Copy(string source, string destination)
{
VisitAll(source, ref destination,
delegate(string dir, ref string destDir, ref bool newDestinationContainer, out bool skip)
{
if (newDestinationContainer || !Directory.Exists(destDir))
{
Directory.CreateDirectory(destDir);
newDestinationContainer = true;
}
skip = false;
},
delegate(string file, ref string destFile, bool newDestinationContainer)
{
File.Copy(file, destFile, !newDestinationContainer);
});
}
Измерение времени копирования:
public static void Measure(string message, Action callback)
{
long mem = GC.GetTotalMemory(true);
long gcCount = GC.CollectionCount(0);
DateTime now = DateTime.Now;
Stopwatch stopwatch = Stopwatch.StartNew();
callback();
TimeSpan elapsed = stopwatch.Elapsed;
TimeSpan elapsedReal = DateTime.Now - now;
// DONTTOUCH:
// It is intended to call GetTotalMemory with forceFullCollection: false
// _before_ GC.CollectionCount call.
BinarySize memDelta = new BinarySize(GC.GetTotalMemory(false) - mem);
long gcCountDelta = GC.CollectionCount(0) - gcCount;
Console.WriteLine(
@"-------
{0}:
Elapsed: {1}, MemDelta: {2}
Elapsed real: {3}, GC count: {4}",
message, elapsed, memDelta, elapsedReal, gcCountDelta);
}
[STAThread]
static void Main(string[] args)
{
string src = @"src path";
string dest = @"dest path";
string dest2 = @"dest path for vb";
Console.WriteLine("Start...");
TestHelper.Measure(
"FsHelper",
() => FileSystemHelper.Copy(src, ref dest, FileSystemConflict.GenerateFileNames));
TestHelper.Measure(
"VB",
() => Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(src, dest2));
TestHelper.Measure(
"Del FsHelper",
() => FileSystemHelper.DeleteIfExists(dest));
TestHelper.Measure(
"Del Directory (fails on read-only)",
() => Directory.Delete(dest2));
Console.WriteLine("Done...");
Console.ReadKey();
}