【问题标题】:DirectoryInfo returns different values when called from different methodsDirectoryInfo 从不同的方法调用时返回不同的值
【发布时间】:2020-09-02 15:05:05
【问题描述】:

我一直在研究一种目录信息查看器。 第一阶段是创建和填充 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。

【问题讨论】:

  • “如果从这里调用,这将不起作用” - 这是什么意思?是否抛出错误?结果与您的预期有何不同?
  • 这些空渔获物是否隐藏了重要信息?请注意,如果抛出异常,所有循环都将被提前终止。
  • “如果从这里调用它不起作用”意味着如果从那里调用它会返回不正确的值。它的要点是,如果我从“CreateDirectoryNode(DirectoryInfo directoryInfo)”运行“DirectoryContent_Operation(directory)”,它会返回 1.2 TB(正确值),如果我从“DirectoryMainOperation(rootDirectoryInfo)”运行它,它会返回 90 MB。
  • 正如@JeffE 所指出的,您可能正在吞下导致循环提前退出的异常 - 即。如果根目录包含您无法枚举的文件或文件夹,那么您只会获得文件系统中问题项之前的目录的聚合结果
  • 循环部分我能理解,但我不明白为什么它嵌套在方法 A 中而在方法 B 中不起作用——它们的构建方式相同(ctrl+c、ctrl+v )。它们具有相同的循环结构,具有相同的 try/catch 异常。

标签: c# wpf directoryinfo


【解决方案1】:

由于UnauthorizedAccessExceptionDirectoryMainOperation 过早返回,因此不会返回所有目录。在DirectoryContent_Operation 中,您可以通过捕获Exception 来吞下每个 异常!

请删除 try-catch (everywhere) 以查看(内部)异常消息究竟是关于什么以及抛出异常的位置..

请注意,吞下异常始终是一种代码异味,并且可能会引入错误,这很难识别。
如果您无法处理异常(将应用程序恢复到稳定状态),则应用程序必须崩溃。然后修复崩溃的原因。
在考虑是否以及如何处理异常之前,请先考虑如何避免它。
同样从性能的角度来看,强烈建议避免昂贵的异常。

您的问题表明,让异常使应用程序崩溃以了解实现错误并防止不必要的静默副作用(例如不完整的目录列表)是多么重要。吞咽异常使您的应用程序处于不可预测的状态,这将导致很多错误行为,这些错误行为可能会被用户一开始并没有注意到。对于使用您的应用程序来管理其业务的客户来说,这可能是非常昂贵的。

为避免在您的情况下使用 UnauthorizedAccessException,您可以使用 DirectoryInfo.EnumerateDirectories(String, EnumerationOptions),它仅适用于 .NET Core(自 2.1 版起)。
它通过默认跳过禁止目录来避免引发UnauthorizedAccessException 异常。

或者,仅枚举顶级目录。然后检查每个子目录是否被禁止,然后再继续递归枚举子目录的顶级目录。下面的递归枚举很有可能会解决你的问题。

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);
  }
}

通常更喜欢DirectoryInfo.EnumerateDirectories 而不是DirectoryInfo.GetDirectories

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-06
    • 1970-01-01
    相关资源
    最近更新 更多