LINQ Queries on Code |
Examples of using LINQ queries on a code object tree.
// LINQ queries can be performed on any collection, and therefore on the immediate child code objects at a // particular point in a code tree using any of the various child object collections. Examples are shown // here both in SQL syntax and also the code alternative. Global queries are shown in the next example. Solution solution = Solution.Load("Nova.Examples.sln"); if (solution != null) { // Use LINQ to find project(s) in a solution var examplesProject = (from project in solution.Projects where project.Name == "Nova.Examples" select project).FirstOrDefault(); var examplesProject2 = solution.Projects.FirstOrDefault(project => project.Name == "Nova.Examples"); if (examplesProject != null) { Log.WriteLine("Found " + examplesProject); // Use LINQ to find CodeUnit(s) in a project var programFile = (from codeUnit in examplesProject.CodeUnits where codeUnit.Name == "Program.cs" select codeUnit).FirstOrDefault(); var programFile2 = examplesProject.CodeUnits.FirstOrDefault(codeUnit => codeUnit.Name == "Program.cs"); if (programFile != null) { Log.WriteLine("Found " + programFile); // Use LINQ to find ClassDecl(s) in a CodeUnit. Note that TypeDecls in CodeUnits can be nested // inside one or more NamespaceDecls - the GetAllChildren<TypeDecl>() method will enumerate all of them. // You can also use 'project.Find()' or '@namespace.Find()' as shown in the FindCodeObjects() example. var programClass = (from classDecl in programFile.GetAllChildren<TypeDecl>().OfType<ClassDecl>() where classDecl.Name == "Program" select classDecl).FirstOrDefault(); var programClass2 = programFile.GetAllChildren<TypeDecl>().OfType<ClassDecl>().FirstOrDefault(classDecl => classDecl.Name == "Program"); if (programClass != null) { Log.WriteLine("Found " + programClass); // Use LINQ to find all static MethodDecls in a ClassDecl var methods = from methodDecl in programClass.Body.OfType<MethodDecl>() where methodDecl.IsStatic select methodDecl; var methods2 = programClass.Body.OfType<MethodDecl>().Where(methodDecl => methodDecl.IsStatic); Log.WriteLine("Found " + methods.Count() + " static MethodDecls in " + programClass); var methodMain = methods.FirstOrDefault(method => method.Name == "Main"); if (methodMain != null) { // Use LINQ to find all top-level Statements in a MethodDecl var statements = from statement in methodMain.Body.OfType<Statement>() select statement; var statements2 = methodMain.Body.OfType<Statement>(); Log.WriteLine("Found " + statements.Count() + " top-level Statements in " + methodMain); } } } } }
// LINQ queries can be performed across nested collections by chaining 'from' clauses and/or with the use of // custom enumerators which recursively traverse trees of collections. Such techniques make it easy to query // code as if it were in a database, searching for patterns of code or calculating advanced code metrics. // The examples shown here simply select all objects of a particular type, but various criteria can be applied // (such as with a 'where' clause) to select whatever sub-set of the objects is desired. Solution solution = Solution.Load("Nova.Examples.sln"); if (solution != null) { // Query all files (CodeUnits) in an entire Solution var allFiles = from project in solution.Projects from codeUnit in project.CodeUnits select codeUnit; Log.WriteLine("Found " + allFiles.Count() + " total files in the entire Solution"); // Query all Namespaces in an entire Solution (declared or imported from referenced assemblies) var allNamespaces = from project in solution.Projects from @namespace in project.GlobalNamespace.GetAllChildren<Namespace>() select @namespace; Log.WriteLine("Found " + allNamespaces.Count() + " Namespaces (declared or imported) in the Solution"); // Note that each Project maintains its own Namespaces because the Types and TypeDecls in the Namespaces depend // upon the referenced assemblies and Projects, which can be different for each Project. So, querying at the // Solution level can result in duplicate Namespaces and/or Types when multiple Projects exist, athough TypeDecls // will generally not be duplicated (but could be if the same source file is included in more than one Project). // Query all types in an entire Solution (imported from referenced assemblies). // Varies if using Mono Cecil vs Reflection vs either of them. var allTypes = from project in solution.Projects from type in project.GlobalNamespace.GetAllChildren<object>() // Or TypeDefinition or Type where type is TypeDefinition || type is Type // Omit if using TypeDefinition or Type above select type; Log.WriteLine("Found " + allTypes.Count() + " imported Types in the Solution"); // Query all TypeDecls in an entire Solution var allTypeDecls = from project in solution.Projects from typeDecl in project.GlobalNamespace.GetAllChildren<TypeDecl>() select typeDecl; Log.WriteLine("Found " + allTypeDecls.Count() + " TypeDecls in the Solution"); Project exampleProject = solution.FindProject("Nova.Examples"); if (exampleProject != null) { // Query all Namespaces in a Project (declared or imported from referenced assemblies and Projects) var namespaces = from @namespace in exampleProject.GlobalNamespace.GetAllChildren<Namespace>() select @namespace; Log.WriteLine("Found " + namespaces.Count() + " Namespaces (declared or imported) in the Project"); // Query all Types in a Project (imported from referenced assemblies). // Varies if using Mono Cecil vs Reflection vs either of them. var types = from type in exampleProject.GlobalNamespace.GetAllChildren<object>() // Or TypeDefinition or Type where type is TypeDefinition || type is Type // Omit if using TypeDefinition or Type above select type; Log.WriteLine("Found " + types.Count() + " imported Types in the Project"); // Query all TypeDecls in a Project (declared or imported from referenced Projects) var typeDecls = from typeDecl in exampleProject.GlobalNamespace.GetAllChildren<TypeDecl>() select typeDecl; Log.WriteLine("Found " + typeDecls.Count() + " TypeDecls (declared or imported) in the Project"); // Query all TypeDecls that are declared in a Project (excluding those imported from other Projects) var declaredTypeDecls = from typeDecl in exampleProject.GetAllDeclaredTypeDecls() select typeDecl; Log.WriteLine("Found " + declaredTypeDecls.Count() + " TypeDecls declared in the Project"); } var timer = new Stopwatch(); // Query ALL CodeObjects in an entire Solution timer.Start(); var codeObjects = from codeObject in solution.GetAllChildren<CodeObject>() select codeObject; double elapsed = timer.Elapsed.TotalSeconds; Log.WriteLine("Found " + codeObjects.Count() + " CodeObjects in the Solution, elapsed time: " + elapsed.ToString("N6")); // Query with parallel processing (PLINQ) for improved performance timer.Restart(); var codeObjects2 = (from codeObject in solution.GetAllChildren<CodeObject>() select codeObject).AsParallel(); elapsed = timer.Elapsed.TotalSeconds; Log.WriteLine("Found " + codeObjects2.Count() + " CodeObjects in the Solution, elapsed time: " + elapsed.ToString("N6") + " (using PLINQ)"); // Query ALL Comments in an entire Solution var comments = from comment in solution.GetAllChildren<Comment>() select comment; Log.WriteLine("Found " + comments.Count() + " Comments in the Solution"); // Query ALL MethodDecls in an entire Solution, with the criteria that they contain more than 2 'if' statements var methodDecls = from methodDecl in solution.GetAllChildren<MethodDecl>() where methodDecl.GetAllChildren<If>().Count() > 2 select methodDecl; Log.WriteLine("Found " + methodDecls.Count() + " MethodDecls in the Solution that contain more than 2 'if' statements"); }