Поделиться через


Практическое руководство. Запрос общего числа байтов в наборе папок (LINQ)

В этом примере показано извлечение общего числа байтов, используемого всеми файлами в определенной папке и всех ее вложенных папках.

Пример

Метод Sum суммирует значения всех элементов, выбранных в предложении select. Этот запрос можно легко изменить для извлечения наибольшего или наименьшего файла в дереве каталогов путем вызова метода Min``1 или Max``1 вместо Sum.

Module QueryTotalBytes
    Sub Main()

        ' Change the drive\path if necessary. 
        Dim root As String = "C:\Program Files\Microsoft Visual Studio 9.0\VB" 

        'Take a snapshot of the folder contents. 
        ' This method assumes that the application has discovery permissions 
        ' for all folders under the specified path. 
        Dim fileList = My.Computer.FileSystem.GetFiles _
                  (root, FileIO.SearchOption.SearchAllSubDirectories, "*.*")

        Dim fileQuery = From file In fileList _
                        Select GetFileLength(file)

        ' Force execution and cache the results to avoid multiple trips to the file system. 
        Dim fileLengths = fileQuery.ToArray()

        ' Find the largest file 
        Dim maxSize = Aggregate aFile In fileLengths Into Max()

        ' Find the total number of bytes 
        Dim totalBytes = Aggregate aFile In fileLengths Into Sum()

        Console.WriteLine("The largest file is " & maxSize & " bytes")
        Console.WriteLine("There are " & totalBytes & " total bytes in " & _
                          fileList.Count & " files under " & root)

        ' Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub 

    ' This method is used to catch the possible exception 
    ' that can be raised when accessing the FileInfo.Length property. 
    Function GetFileLength(ByVal filename As String) As Long 
        Dim retval As Long 
        Try 
            Dim fi As New System.IO.FileInfo(filename)
            retval = fi.Length
        Catch ex As System.IO.FileNotFoundException
            ' If a file is no longer present, 
            ' just return zero bytes. 
            retval = 0
        End Try 

        Return retval
    End Function 
End Module
class QuerySize
{
    public static void Main()
    {
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\VC#";

        // Take a snapshot of the file system. 
        // This method assumes that the application has discovery permissions 
        // for all folders under the specified path.
        IEnumerable<string> fileList = System.IO.Directory.GetFiles(startFolder, "*.*", System.IO.SearchOption.AllDirectories);

        var fileQuery = from file in fileList
                        select GetFileLength(file);

        // Cache the results to avoid multiple trips to the file system. 
        long[] fileLengths = fileQuery.ToArray();

        // Return the size of the largest file 
        long largestFile = fileLengths.Max();

        // Return the total number of bytes in all the files under the specified folder. 
        long totalBytes = fileLengths.Sum();

        Console.WriteLine("There are {0} bytes in {1} files under {2}",
            totalBytes, fileList.Count(), startFolder);
        Console.WriteLine("The largest files is {0} bytes.", largestFile);

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    // This method is used to swallow the possible exception 
    // that can be raised when accessing the System.IO.FileInfo.Length property. 
    static long GetFileLength(string filename)
    {
        long retval;
        try
        {
            System.IO.FileInfo fi = new System.IO.FileInfo(filename);
            retval = fi.Length;
        }
        catch (System.IO.FileNotFoundException)
        {
            // If a file is no longer present, 
            // just add zero bytes to the total.
            retval = 0;
        }
        return retval;
    }
}

Если требуется только подсчитать число байтов в указанном дереве каталогов, можно сделать это более эффективно без создания запроса LINQ, который вносит дополнительные издержки, связанные с созданием коллекции списков в качестве источника данных. Практичность подхода LINQ увеличивается по мере усложнения запроса или при необходимости выполнения нескольких запросов к одному источнику данных.

Для получения длины файла запрос вызывает отдельный метод. Это необходимо для обработки возможных исключений, которые могут возникнуть из-за удаления файла в другом потоке после создания объекта FileInfo вызовом GetFiles. Даже если объект FileInfo уже создан, может возникнуть исключение, так как объект FileInfo будет пытаться обновить свойство Length, используя самую последнюю длину при первом обращении к свойству. Поместив эту операцию в блоке try-catch вне запроса, будет выполнено правило исключения использования в запросах операций, которые могут вызвать побочные эффекты. В целом, необходимо соблюдать осторожность при перехвате исключений, чтобы убедиться, что приложение не остается в неизвестном состоянии.

Компиляция кода

  • Создайте проект Visual Studio, предназначенный для платформы .NET Framework версии 3.5. По умолчанию в проекте имеются ссылка на файл System.Core.dll и директива using (C#) или оператор Imports (Visual Basic) для пространства имен System.Linq. При работе с проектами C# добавьте директиву using для пространства имен System.IO.

  • Скопируйте этот код в проект.

  • Нажмите клавишу F5, чтобы скомпилировать и выполнить программу.

  • Нажмите любую клавишу для выхода из окна консоли.

Отказоустойчивость

Для интенсивного использования операций запроса к содержимому нескольких типов документов и файлов, рассмотрите возможность использования средства поиска Windows Desktop Search.

См. также

Основные понятия

LINQ to Objects

LINQ и каталоги файлов