C# 使用Roslyn在Visual Studio解决方案中查找所有引用 太长,读不下去了
如何在Visual Studio解决方案中找到索引属性C# 使用Roslyn在Visual Studio解决方案中查找所有引用 太长,读不下去了,c#,visual-studio,roslyn,projects-and-solutions,C#,Visual Studio,Roslyn,Projects And Solutions,如何在Visual Studio解决方案中找到索引属性Microsoft.Extensions.Localization.IStringLocalizer.Item[string]引用的所有常量字符串参数?所有源代码都是用C#编写的。该解决方案还必须支持MVC razor视图 附加信息 我相信罗斯林就是这个问题的答案。一、 但是,我还没有找到通过API实现这一点的方法。我还不确定是使用语法树、编译还是语义模型。以下是基于stackoverflow的其他问答的尝试。我们非常感谢您的任何帮助:-)如
Microsoft.Extensions.Localization.IStringLocalizer.Item[string]
引用的所有常量字符串参数?所有源代码都是用C#编写的。该解决方案还必须支持MVC razor视图
附加信息
我相信罗斯林就是这个问题的答案。一、 但是,我还没有找到通过API实现这一点的方法。我还不确定是使用语法树、编译还是语义模型。以下是基于stackoverflow的其他问答的尝试。我们非常感谢您的任何帮助:-)如果您对此感到好奇,您可以阅读此需求的原因
更新:解决方法
尽管@Oxoron提供了巨大的帮助,我还是选择了一个简单的解决方法。目前,Roslyn没有使用SymbolFinder.FindReferencesAsync
找到任何引用。这似乎与“静默”msbuild失败有关。这些错误如下所示:
msWorkspace.WorkspaceFailed += (sender, eventArgs) =>
{
Console.Error.WriteLine($"{eventArgs.Diagnostic.Kind}: {eventArgs.Diagnostic.Message}");
Console.Error.WriteLine();
};
public void ParseSource()
{
var sourceFiles = from f in Directory.GetFiles(SourceDir, "*.cs*", SearchOption.AllDirectories)
where f.EndsWith(".cs") || f.EndsWith(".cshtml")
where !f.Contains(@"\obj\") && !f.Contains(@"\packages\")
select f;
// _["Hello, World!"]
// _[@"Hello, World!"]
// _localizer["Hello, World!"]
var regex = new Regex(@"_(localizer)?\[""(.*?)""\]");
foreach (var sourceFile in sourceFiles)
{
foreach (var line in File.ReadLines(sourceFile))
{
var matches = regex.Matches(line);
foreach (Match match in matches)
{
var resourceKey = GetResourceKeyFromFileName(sourceFile);
var key = match.Groups[2].Value;
Console.WriteLine($"{resourceKey}: {key}");
}
}
}
}
及
我的解决方法大致如下:
msWorkspace.WorkspaceFailed += (sender, eventArgs) =>
{
Console.Error.WriteLine($"{eventArgs.Diagnostic.Kind}: {eventArgs.Diagnostic.Message}");
Console.Error.WriteLine();
};
public void ParseSource()
{
var sourceFiles = from f in Directory.GetFiles(SourceDir, "*.cs*", SearchOption.AllDirectories)
where f.EndsWith(".cs") || f.EndsWith(".cshtml")
where !f.Contains(@"\obj\") && !f.Contains(@"\packages\")
select f;
// _["Hello, World!"]
// _[@"Hello, World!"]
// _localizer["Hello, World!"]
var regex = new Regex(@"_(localizer)?\[""(.*?)""\]");
foreach (var sourceFile in sourceFiles)
{
foreach (var line in File.ReadLines(sourceFile))
{
var matches = regex.Matches(line);
foreach (Match match in matches)
{
var resourceKey = GetResourceKeyFromFileName(sourceFile);
var key = match.Groups[2].Value;
Console.WriteLine($"{resourceKey}: {key}");
}
}
}
}
当然,该解决方案不是防弹的,它依赖于命名约定,不处理多行逐字字符串。但它可能会为我们做这项工作:-)看看这些问题,它们将有助于索引器
确定名称空间-这有点困难。
您可以使用如下代码确定它
int spanStart = symbol.Locations[0].Location.SourceSpan.Start;
Document doc = symbol.Locations[0].Location.Document;
var indexerInvokation = doc.GetSyntaxRootAsync().Result.DescendantNodes()
.FirstOrDefault(node => node.GetLocation().SourceSpan.Start == spanStart );
之后,只需查找索引器调用父节点,直到MethodDeclarationSyntax、ClassDeclarationSyntax等
Upd1.
测试项目代码:
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
int test0 = new A().GetInt();
int test1 = new IndexedUno()[2];
int test2 = new IndexedDo()[2];
}
}
public interface IIndexed
{
int this[int i] { get; }
}
public class IndexedUno : IIndexed
{
public int this[int i] => i;
}
public class IndexedDo : IIndexed
{
public int this[int i] => i;
}
public class A
{
public int GetInt() { return new IndexedUno()[1]; }
}
public class B
{
public int GetInt() { return new IndexedDo()[4]; }
}
}
搜索代码:
using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.MSBuild;
namespace AnalyzeIndexers
{
class Program
{
static void Main(string[] args)
{
string solutionPath = @"PathToSolution.sln";
var msWorkspace = MSBuildWorkspace.Create();
var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result;
foreach (var project in solution.Projects.Where(p => p.AssemblyName.StartsWith("TestApp")))
{
var compilation = project.GetCompilationAsync().Result;
var interfaceType = compilation.GetTypeByMetadataName("TestApp.IIndexed");
var indexer = interfaceType
.GetMembers()
.OfType<IPropertySymbol>()
.First(member => member.IsIndexer);
var indexReferences = SymbolFinder.FindReferencesAsync(indexer, solution).Result.ToList();
foreach (var indexReference in indexReferences)
{
foreach (ReferenceLocation indexReferenceLocation in indexReference.Locations)
{
int spanStart = indexReferenceLocation.Location.SourceSpan.Start;
var doc = indexReferenceLocation.Document;
var indexerInvokation = doc.GetSyntaxRootAsync().Result
.DescendantNodes()
.FirstOrDefault(node => node.GetLocation().SourceSpan.Start == spanStart);
var className = indexerInvokation.Ancestors()
.OfType<ClassDeclarationSyntax>()
.FirstOrDefault()
?.Identifier.Text ?? String.Empty;
var @namespace = indexerInvokation.Ancestors()
.OfType<NamespaceDeclarationSyntax>()
.FirstOrDefault()
?.Name.ToString() ?? String.Empty;
Console.WriteLine($"{@namespace}.{className} : {indexerInvokation.GetText()}");
}
}
}
Console.WriteLine();
Console.ReadKey();
}
}
}
使用系统;
使用System.Linq;
使用Microsoft.CodeAnalysis;
使用Microsoft.CodeAnalysis.CSharp.Syntax;
使用Microsoft.CodeAnalysis.FindSymbols;
使用Microsoft.CodeAnalysis.MSBuild;
命名空间分析器索引器
{
班级计划
{
静态void Main(字符串[]参数)
{
字符串solutionPath=@“PathToSolution.sln”;
var msWorkspace=MSBuildWorkspace.Create();
var solution=msWorkspace.OpenSolutionAsync(solutionPath).Result;
foreach(solution.Projects.Where(p=>p.AssemblyName.StartsWith(“TestApp”))中的var项目)
{
var compilation=project.GetCompilationAsync().Result;
var interfaceType=compilation.GetTypeByMetadataName(“TestApp.IIndexed”);
变量索引器=接口类型
.GetMembers()
第()类
.First(member=>member.IsIndexer);
var indexReferences=SymbolFinder.FindReferencesAsync(索引器,解决方案).Result.ToList();
foreach(索引引用中的变量索引引用)
{
foreach(引用位置indexReference.Locations中的引用位置)
{
int spanStart=indexReferenceLocation.Location.SourceSpan.Start;
var doc=indexReferenceLocation.Document;
var indexerInvokation=doc.GetSyntaxRootAsync().Result
.DegeneratNodes()的
.FirstOrDefault(node=>node.GetLocation().SourceSpan.Start==spanStart);
var className=indexerInvokation.祖先()
第()类
.FirstOrDefault()
?.Identifier.Text??String.Empty;
var@namespace=indexerInvokation.concenters()
第()类
.FirstOrDefault()
?.Name.ToString()?.String.Empty;
WriteLine($“{@namespace}.{className}:{indexerInvokation.GetText()}”);
}
}
}
Console.WriteLine();
Console.ReadKey();
}
}
}
看看var索引器=。。。代码-它从类型中提取索引器。也许您需要使用getter\setter
其他关注点:索引器调用计算。我们获取SyntaxRoot的频率太高,可能您需要某种缓存
下一步:类和命名空间搜索。我没有找到方法,但建议不要找到它:可以有属性、其他索引器、匿名方法使用您的索引器。如果你真的不在乎这一点,那就去找MethodDeclarationSyntax类型的祖先。Roslyn Analysys的第一条规则:编写你想自动显示的代码。你想找到像localizer[“anyParam”]这样的代码吗?是的,确切地说是@Oxoron:-),我不需要代码分析来处理除常量字符串调用之外的任何其他特殊情况。谢谢@Oxoron。我认为这两个链接没有帮助。我真的很难得到索引器调用的符号。您知道这里的OfType generic参数应该是什么吗?或者我应该完全不同地去做<代码>变量调用=tree.GetRoot().degenantNodes().OfType()再次使用代码>Thx@Oxoron。你更新中的例子似乎正是我所需要的。然而,我在这项任务上花费了相当多的时间,目前我的解决方案存在很多错误。这些错误似乎与使用VS和Roslyn使用的MSBuild构建解决方案的方式不同有关。唉,我决定进行一种变通(请参阅我的问题更新)。但是再次感谢你的帮助!