我是roslyn的初学者,因此我尝试通过制作一个非常简单的控制台应用程序来开始学习它,该应用程序在著名的教程网站中进行了介绍。 (https://riptutorial.com/roslyn/example/16545/introspective-analysis-of-an-analyzer-in-csharp),效果不佳。
我创建的Cosole应用程序是.NET Framework(目标Framework版本是4.7.2),而不是.NET Core或.NET标准。 我添加了NuGet包Microsoft.CodeAnalysis和Microsoft.CodeAnalysis.Workspaces.MSBuild,然后编写了一个简单的代码,如下所示。
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System;
using System.Linq;
namespace SimpleRoslynConsole
{
class Program
{
static void Main(string[] args)
{
// Declaring a variable with the current project file path.
// *** You have to change this path to fit your development environment.
const string projectPath =
@"C:\Users\[MyName]\Source\Repos\RoslynTrialConsole01\RoslynTrialConsole01.csproj";
var workspace = MSBuildWorkspace.Create();
var project = workspace.OpenProjectAsync(projectPath).Result;
// [**1]Getting the compilation.
var compilation = project.GetCompilationAsync().Result;
// [**2]As this is a simple single file program, the first syntax tree will be the current file.
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault();
if (syntaxTree != null)
{
var rootSyntaxNode = syntaxTree.GetRootAsync().Result;
var firstLocalVariablesDeclaration = rootSyntaxNode.DescendantNodesAndSelf()
.OfType<LocalDeclarationStatementSyntax>().First();
var firstVariable = firstLocalVariablesDeclaration.Declaration.Variables.First();
var variableInitializer = firstVariable.Initializer.Value.GetFirstToken().ValueText;
Console.WriteLine(variableInitializer);
}
else
{
Console.WriteLine("Could not get SyntaxTrees from this projects.");
}
Console.WriteLine("Hit any key.");
Console.ReadKey();
}
}
}
我的问题是,Compilation对象的SyntaxTrees属性在[** 2]标记中返回null。自然,遵循FirstOrDefault方法将返回null。
我尝试了其他一些代码。我发现可以使用CSharpSyntaxTree.ParseText方法从CSharp代码文本中获取SyntaxTree。但是按照
的顺序,我无法从源代码中获取任何信息var workspace = MSBuildWorkspace.Create();
var project = workspace.OpenProjectAsync(projectPath).Result;
var compilation = project.GetCompilationAsync().Result;
我想知道的是,如果我错过了使用上述过程从源代码中获取语法信息的事情。
我会感谢有人给我一个很好的建议。
答案 0 :(得分:1)
我认为问题在于 .net 框架项目的源文件路径位于 .csproj 中。并立即打开项目。 对于 .net 核心项目,您没有此类信息,也许这就是 Workspace 实例不知道要加载什么,因此什么都不加载的原因。 至少将 .cs 文件指定为添加的文档就可以了。尝试应用此:
static class ProjectExtensions
{
public static Project AddDocuments(this Project project, IEnumerable<string> files)
{
foreach (string file in files)
{
project = project.AddDocument(file, File.ReadAllText(file)).Project;
}
return project;
}
private static IEnumerable<string> GetAllSourceFiles(string directoryPath)
{
var res = Directory.GetFiles(directoryPath, "*.cs", SearchOption.AllDirectories);
return res;
}
public static Project WithAllSourceFiles(this Project project)
{
string projectDirectory = Directory.GetParent(project.FilePath).FullName;
var files = GetAllSourceFiles(projectDirectory);
var newProject = project.AddDocuments(files);
return newProject;
}
}
WithAllsourceFiles 方法将返回您的项目,该项目的编译将依次包含您期望的所有语法树,就像您在 Visual Studio 中一样
答案 1 :(得分:0)
除非您在应用程序的app.config文件中具有与msbuild.exe.config相同的重定向,否则MsBuildWorkspace将无法正常工作。如果没有重定向,则可能无法加载msbuild库。您需要找到系统上的msbuild.exe.config文件,并将与Microsoft.Build程序集相关的<assemblyBinding>
元素复制到app.config中。确保将它们放置在正确的元素配置/运行时下。
答案 2 :(得分:0)
我在网上搜索了各种示例程序,发现了最可靠,最安全的方法。解决方案是创建一个静态方法,该方法将在指定的File中返回SyntaxTrees,如下所示。
private static Compilation CreateTestCompilation()
{
var found = false;
var di = new DirectoryInfo(Environment.CurrentDirectory);
var fi = di.GetFiles().Where((crt) => { return crt.Name.Equals("program.cs", StringComparison.CurrentCultureIgnoreCase); }).FirstOrDefault();
while ((fi == null) || (di.Parent == null))
{
di = new DirectoryInfo(di.Parent.FullName);
fi = di.GetFiles().Where((crt) => { return crt.Name.Equals("program.cs", StringComparison.CurrentCultureIgnoreCase); }).FirstOrDefault();
if (fi != null)
{
found = true;
break;
}
}
if (!found)
{
return null;
}
var targetPath = di.FullName + @"\Program.cs";
var targetText = File.ReadAllText(targetPath);
var targetTree =
CSharpSyntaxTree.ParseText(targetText)
.WithFilePath(targetPath);
var target2Path = di.FullName + @"\TypeInferenceRewriter.cs";
var target2Text = File.ReadAllText(target2Path);
var target2Tree =
CSharpSyntaxTree.ParseText(target2Text)
.WithFilePath(target2Path);
SyntaxTree[] sourceTrees = { programTree, target2Tree };
MetadataReference mscorlib =
MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
MetadataReference codeAnalysis =
MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
MetadataReference csharpCodeAnalysis =
MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location);
MetadataReference[] references = { mscorlib, codeAnalysis, csharpCodeAnalysis };
return CSharpCompilation.Create("TransformationCS",
sourceTrees,
references,
new CSharpCompilationOptions(
OutputKind.ConsoleApplication));
}
调用者程序将是这样。
static void Main(string[] args)
{
var test = CreateTestCompilation();
if (test == null)
{
return;
}
foreach (SyntaxTree sourceTree in test.SyntaxTrees)
{
Console.WriteLine(souceTree.ToFullString());
}
}
当然,需要进行很多改进才能使其实用。