А как бы вы написали?
От: Sinix  
Дата: 19.12.09 04:58
Оценка:
Вытащил из 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();
    }
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.