首页 > 解决方案 > DirectoryInfo 从不同方法调用时返回不同的值

问题描述

我一直在研究一种目录信息查看器。第一阶段是创建和填充 TreeView 模型,在编码期间,我嵌套了一种方法来收集有关该目录上文件的信息。一切正常,返回的值我们是正确的等等。

现在在代码清理阶段,当我尝试从 TreeView 创建中获取该方法时,我得到的值甚至不接近原始值(比如 90 MB 而不是 1.2 TB)。

下面的代码标记了问题:

private static int itemCount = 0;
private static long folderSizeInfo = 0;

public static void ListDirectory(string path)
    {
        (...)

        var rootDirectoryInfo = new DirectoryInfo(path);
        MainWindow.newWindowReport.Drzewko.Items.Add(CreateDirectoryNode(rootDirectoryInfo));

        //this does not work if invoked from here <----------------- FIX ME!
        //DirectoryMainOperation(rootDirectoryInfo);

        //need to run this once more to get data from root
        DirectoryContent_Operation(rootDirectoryInfo);

        (...)
    }

    private static TreeViewItem CreateDirectoryNode(DirectoryInfo directoryInfo)
    {
        var directoryNode = new TreeViewItem { Header = directoryInfo.Name };

        try
        {
            foreach (var directory in directoryInfo.GetDirectories())
            {
                if (!IsIgnorable(directory.Name))
                {
                    directoryNode.Items.Add(CreateDirectoryNode(directory));

                    //this method somehow only works here <------------------------ FIX ME!
                    DirectoryContent_Operation(directory);
                }
            }
        }
        catch (UnauthorizedAccessException)
        {
            //Console.WriteLine("Path is not accessible: {0}", i);
        }

        return directoryNode;
    }

    //-------------------TO BE FIXED------------------
    private static void DirectoryMainOperation(DirectoryInfo directoryInfo)
    {
        try
        {
            foreach (var directory in directoryInfo.GetDirectories())
            {
                if (!IsIgnorable(directory.Name))
                {
                    DirectoryContent_Operation(directory);
                }
            }
        }
        catch (UnauthorizedAccessException)
        {
            //Console.WriteLine("Path is not accessible: {0}", i);
        }
    }

    private static void DirectoryContent_Operation(DirectoryInfo targetDir)
    {
        try
        {
            foreach (var file in targetDir.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
            {
                itemCount++;
                folderSizeInfo += file.Length;

                fileTable.Add(new FileExtension_List
                {
                    FileFormat = file.Extension,
                    Category = extDB.Translation(file.Extension.ToUpper()),
                    Count = 1,
                    TotalSize = file.Length
                });
            }
        }
        catch (UnauthorizedAccessException)
        {
        }
        catch (Exception ex)
        {
        }
    }

它的要点是,如果我从以下位置调用“DirectoryContent_Operation(directory)”:

  1. “CreateDirectoryNode(DirectoryInfo directoryInfo)”它返回 1.2 TB(正确值)
  2. “DirectoryMainOperation(DirectoryInfo directoryInfo)”它返回 90 MB。

标签: c#wpfdirectoryinfo

解决方案


From what it looks like DirectoryMainOperation returns prematurely due to an UnauthorizedAccessException, hence not returning all directories. In DirectoryContent_Operation you swallow every exception by catching Exception!

Please remove the try-catch (everywhere) to see what the (inner) exception message exactly is about and where the exception is thrown..

Note that swallowing exceptions is always a code smell and will likely introduce bugs, that can be really hard to identify.
If you can't handle the exception (put the application back into a stable state) the application must crash. Then fix the reason for the crash.
Before you think about if and how to handle an exception, think about how to avoid it.
Also from a performance perspective, it is highly recommended to avoid expensive exceptions.

Your problem shows how important it is to let exceptions crash the application in order to learn about implementation errors and also to prevent unwanted silent side effects like your incomplete directory list. Swallowing exceptions has put your application into an unpredictable state, which will lead to a lot faulty behavior, which may be unnoticed by users at first. This ca be very costly for a customer who uses your application to manage his business.

To avoid the UnauthorizedAccessException in your case, you can use DirectoryInfo.EnumerateDirectories(String, EnumerationOptions), which is available for .NET Core only (since version 2.1).
It avoids throwing an UnauthorizedAccessException exception by skipping forbidden directories by default.

Alternatively, only enumerate top level directories. Then check each child directory if it's forbidden, before you continue to recursively enumerate the child's top level directories. It is very likely that the following recursive enumeration will solve your problem.

public void ListDirectory(string path)
{
  var rootDirectoryInfo = new DirectoryInfo(path);    
  var rootDirectoryNode = new TreeViewItem { Header = directoryInfo.Name };     
  MainWindow.newWindowReport.Drzewko.Items.Add(rootDirectoryNode);
  CreateDirectoryNode(rootDirectoryInfo, rootDirectoryNode);
}

private static void CreateDirectoryNode(DirectoryInfo parentDirectory, TreeViewItem parentDirectoryNode)
{
  foreach (DirectoryInfo childDirectory in parentDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
  {
    var childDirectoryNode = new TreeViewItem { Header = childDirectory.Name };

    parentDirectoryNode.Items.Add(childDirectoryNode);

    // Don't enter forbidden directories
    // and optionally hidden directories too
    if (childDirectory.Attributes.HasFlag(FileAttributes.System) 
      || childDirectory.Attributes.HasFlag(FileAttributes.Hidden))
    {  
      continue;
    }

    // Recursively iterate over child's subdirectories
    CreateDirectoryNode(childDirectory, childDirectoryNode);
    DirectoryContent_Operation(childDirectory);
  }
}

private static void DirectoryMainOperation(DirectoryInfo parentDirectory)
{
  foreach (DirectoryInfo childDirectory in parentDirectory.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
  {
    if (!IsIgnorable(childDirectory.Name))
    {
      DirectoryContent_Operation(childDirectory);
    }

    // Don't enter forbidden directories 
    // and optionally hidden directories too
    if (childDirectory.Attributes.HasFlag(FileAttributes.System) 
      || childDirectory.Attributes.HasFlag(FileAttributes.Hidden))
    {  
      continue;
    }
    
    // Recursively iterate over child's subdirectories
    DirectoryMainOperation(childDirectory);
  }
}

Generally prefer DirectoryInfo.EnumerateDirectories over DirectoryInfo.GetDirectories.


推荐阅读