Page 18 - MSDN Magazine, May 2017
P. 18
Figure 3 Generating a New Syntax Node
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
namespace RoslynCore {
public static class ViewModelGeneration {
public static SyntaxNode GenerateViewModel(SyntaxNode node) {
// Find the first class in the syntax node var classNode = node.DescendantNodes()
.OfType<ClassDeclarationSyntax>().FirstOrDefault();
if(classNode!=null) {
// Get the name of the model class
string modelClassName = classNode.Identifier.Text;
// The name of the ViewModel class
string viewModelClassName = $"{modelClassName}ViewModel"; // Only for demo purposes, pluralizing an object is done by // simply adding the "s" letter. Consider proper algorithms string newImplementation =
$@"public class {viewModelClassName} : INotifyPropertyChanged
{{
public event PropertyChangedEventHandler PropertyChanged; // Raise a property change notification
protected virtual void OnPropertyChanged(string propname) {{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname)); }}
private ObservableCollection<{modelClassName}> _{modelClassName}s; public ObservableCollection<{modelClassName}> {modelClassName}s {{
get {{ return _{modelClassName}s; }} set
{{
_{modelClassName}s = value;
OnPropertyChanged(nameof({modelClassName}s)); }}
}}
public {viewModelClassName}() {{
// Implement your logic to load a collection of items }}
}}
";
var newClassNode = CSharpSyntaxTree.ParseText(newImplementation).GetRoot() .DescendantNodes().OfType<ClassDeclarationSyntax>() .FirstOrDefault();
// Retrieve the parent namespace declaration if(!(classNode.Parent is NamespaceDeclarationSyntax)) return null;
var parentNamespace = (NamespaceDeclarationSyntax)classNode.Parent; // Add the new class to the namespace and adjust the white spaces var newParentNamespace =
parentNamespace.AddMembers(newClassNode).NormalizeWhitespace(); return newParentNamespace;
} }
else {
return null; }
} }
}
Note that adding a reference to the Microsoft.CodeAnalysis.CSharp package allows you to access the C# compiler’s APIs, and that the System.Runtime.Loader package is required for Reflection and will be used later in the article.
user input. With the code analysis APIs, you can parse the source text and generate a new syntax node that the compiler can under- stand and manipulate. For example, consider the code shown in
When you save your changes, Visual Studio Code will detect missing NuGet packages and will offer to restore them.
Code Analysis:
Parsing Source Text and
Generating Syntax Nodes
The first example is about code analysis, and demonstrates how to parse source code text and gener- ate new syntax nodes. For instance, imagine you have the following simple business object and you want to generate a View Model
class based on it:
namespace Models {
public class Item {
public string ItemName { get; set } }
}
The text for this business object might come from different sources, such as a C# code file or a string in your code or even from
Figure 4 The View Model Class Has Been Correctly Generated
14 msdn magazine
.NET Core