更新:2007 年 11 月
此示例演示如何使用 LINQ 对文件或文件夹列表执行高级分组和排序操作。此外,它还演示如何使用 Skip<TSource> 和 Take<TSource> 方法对控制台窗口中的输出进行分页。
示例
下面的查询演示如何按文件扩展名对指定目录树的内容进行分组。
Module GroupByExtension
    Public Sub Main()
        ' Root folder to query, along with all subfolders.
        Dim startFolder As String = "C:\program files\Microsoft Visual Studio 9.0\VB\"
        ' Used in WriteLine() to skip over startfolder in output lines.
        Dim rootLength As Integer = startFolder.Length
        ' Take a snapshot of the file system.
        Dim fileList As IEnumerable(Of System.IO.FileInfo) = GetFiles(startFolder)
        ' Create the query.
        Dim queryGroupByExt = From file In fileList _
                          Group By file.Extension.ToLower() Into fileGroup = Group _
                          Order By ToLower _
                          Select fileGroup
        ' Execute the query. By storing the result we can
        ' page the display with good performance.
        Dim groupByExtList = queryGroupByExt.ToList()
        ' Display one group at a time. If the number of 
        ' entries is greater than the number of lines
        ' in the console window, then page the output.
        Dim trimLength = startFolder.Length
        PageOutput(groupByExtList, trimLength)
    End Sub
    ' Function to retrieve a list of files. Note that this is a copy
    ' of the file information.
    Function GetFiles(ByVal root As String) As IEnumerable(Of System.IO.FileInfo)
        Return From file In My.Computer.FileSystem.GetFiles _
                  (root, FileIO.SearchOption.SearchAllSubDirectories, "*.*") _
               Select New System.IO.FileInfo(file)
    End Function
    ' Pages console diplay for large query results. No more than one group per page.
    ' This sub specifically works with group queries of FileInfo objects
    ' but can be modified for any type.
    Sub PageOutput(ByVal groupQuery, ByVal charsToSkip)
        ' "3" = 1 line for extension key + 1 for "Press any key" + 1 for input cursor.
        Dim numLines As Integer = Console.WindowHeight - 3
        ' Flag to indicate whether there are more results to diplay
        Dim goAgain As Boolean = True
        For Each fg As IEnumerable(Of System.IO.FileInfo) In groupQuery
            ' Start a new extension at the top of a page.
            Dim currentLine As Integer = 0
            Do While (currentLine < fg.Count())
                Console.Clear()
                Console.WriteLine(fg(0).Extension)
                ' Get the next page of results
                ' No more than one filename per page
                Dim resultPage = From file In fg _
                                Skip currentLine Take numLines
                ' Execute the query. Trim the display output.
                For Each line In resultPage
                    Console.WriteLine(vbTab & line.FullName.Substring(charsToSkip))
                Next
                ' Advance the current position
                currentLine = numLines + currentLine
                ' Give the user a chance to break out of the loop
                Console.WriteLine("Press any key for next page or the 'End' key to exit.")
                Dim key As ConsoleKey = Console.ReadKey().Key
                If key = ConsoleKey.End Then
                    goAgain = False
                    Exit For
                End If
            Loop
        Next
    End Sub
End Module
class GroupByExtension
{
    // This query will sort all the files under the specified folder
    //  and subfolder into groups keyed by the file extension.
    private static void Main()
    {
        // Take a snapshot of the file system.
        string startFolder = @"c:\program files\Microsoft Visual Studio 9.0\Common7";
        // Used in WriteLine to trim output lines.
        int trimLength = startFolder.Length;
        // Take a snapshot of the file system.
        IEnumerable<System.IO.FileInfo> fileList = GetFiles(startFolder);
        // Create the query.
        var queryGroupByExt =
            from file in fileList
            group file by file.Extension.ToLower() into fileGroup
            orderby fileGroup.Key
            select fileGroup;
        // Display one group at a time. If the number of 
        // entries is greater than the number of lines
        // in the console window, then page the output.
        PageOutput(trimLength, queryGroupByExt);
    }
    // This method specifically handles group queries of FileInfo objects with string keys.
    // It can be modified to work for any long listings of data. Note that explicit typing
    // must be used in method signatures. The groupbyExtList parameter is a query that produces
    // groups of FileInfo objects with string keys.
    private static void PageOutput( int rootLength, 
                                    IEnumerable<System.Linq.IGrouping<string, System.IO.FileInfo>> groupByExtList)
    {
        // Flag to break out of paging loop.
        bool goAgain = true;
        // "3" = 1 line for extension + 1 for "Press any key" + 1 for input cursor.
        int numLines = Console.WindowHeight - 3;
        // Iterate through the outer collection of groups.
        foreach (var filegroup in groupByExtList)
        {
            // Start a new extension at the top of a page.
            int currentLine = 0;
            // Output only as many lines of the current group as will fit in the window.
            do
            {
                Console.Clear();
                Console.WriteLine(filegroup.Key == String.Empty ? "[none]" : filegroup.Key);
                // Get 'numLines' number of items starting at number 'currentLine'.
                var resultPage = filegroup.Skip(currentLine).Take(numLines);
                //Execute the resultPage query
                foreach (var f in resultPage)
                {
                    Console.WriteLine("\t{0}", f.FullName.Substring(rootLength));
                }
                // Increment the line counter.
                currentLine += numLines;
                // Give the user a chance to escape.
                Console.WriteLine("Press any key to continue or the 'End' key to break...");
                ConsoleKey key = Console.ReadKey().Key;
                if (key == ConsoleKey.End)
                {
                    goAgain = false;
                    break;
                }
            } while (currentLine < filegroup.Count());
            if (goAgain == false)
                break;
        }
    }
    // This method assumes that the application has discovery 
    // permissions for all folders under the specified path.
    static IEnumerable<System.IO.FileInfo> GetFiles(string path)
    {
        if (!System.IO.Directory.Exists(path))
            throw new System.IO.DirectoryNotFoundException();
        string[] fileNames = null;
        List<System.IO.FileInfo> files = new List<System.IO.FileInfo>();
        fileNames = System.IO.Directory.GetFiles(path, "*.*", System.IO.SearchOption.AllDirectories);
        foreach (string name in fileNames)
        {
            files.Add(new System.IO.FileInfo(name));
        }
        return files;
    }
}
此程序的输出可能会很长,具体取决于本地文件系统的细节以及 startFolder 的设置。为了使您可以查看所有结果,此示例还演示如何按页查看结果。这些方法可应用于 Windows 和 Web 应用程序。请注意,由于代码将对组中的项进行分页,因此需要嵌套的 foreach 循环。此外,还会使用某他某个逻辑来计算列表中的当前位置,以及使用户可以停止分页并退出程序。在这种特定情况下,将针对原始查询的缓存结果运行分页查询。在其他上下文(如 LINQ to SQL)中,不需要这种缓存。
编译代码
- 创建一个面向 .NET Framework 3.5 版的 Visual Studio 项目。默认情况下,该项目具有对 System.Core.dll 的引用和针对 System.Linq 命名空间的 using 指令 (C#) 或 Imports 语句 (Visual Basic)。在 C# 项目中,添加针对 System.IO 命名空间的 using 指令。 
- 将这段代码复制到项目中。 
- 按 F5 编译并运行程序。 
- 按任意键退出控制台窗口。 
可靠编程
若要对多种类型的文档和文件的内容执行密集型查询操作,请考虑使用 Windows Desktop Search(Windows 桌面搜索)引擎。