我正在使用Roslyn从文本中解析C#代码。一些代码具有围绕多个类的区域。例如:
#region Classes
public class MyClass
{
}
public class MyClass2
{
#region Methods
#endregion
}
#endregion
我想删除类周围的区域(上例中的“类”),但保留内部区域,就像上面示例中名为“Methods”的区域一样。我怎么能这样做呢?
答案 0 :(得分:3)
区域相当奇特,因为它们不遵循通常的树形结构。例如,您可以创建一个这样的构造:
public class TestClass{
public void TestMethod(){
#region TestRegion
}
}
#endregion
这仍然有效。考虑到这一点,在分析区域时还有一个问题:它们是琐事中的节点。因此,要获取相关节点,您可以使用SyntaxRewriter(并传递构造函数" true"启用琐事分析)或使用node.DescendantNodes(descendIntoTrivia:true)查找后代节点。
由于区域的开始和结束可能位于文件的任何位置,因此您应始终在语法树的根目录开始分析,以确保您找到区域的结束/开始。
为了找到该地区,您可以覆盖VisitRegionDirectiveTrivia以及VisitEndRegionDirectiveTrivia。由于RegionTrivia的开始和结束彼此不了解,您需要自己匹配它们。在下面的例子中,我简单地计算了我已经传递了多少个区域,并记下了一个#endregion位置列表,这些位置在走出一个区域时应该被删除。
为了识别相关区域,我提供了两种方法:您可以使用区域名称或识别附加的节点是否为ClassDeclaration。
这两种方法都没有在类声明之前考虑诸如属性声明之类的情况。如果你想要处理这个问题,你需要看看兄弟节点并检查它们是否在该区域的范围内开始。
private class RegionSyntaxRewriter : CSharpSyntaxRewriter
{
int currentPosition = 0;
private List<int> EndRegionsForDeletion = new List<int>();
private string deletedRegion;
private bool useRegionNameForAnalysis = false;
public RegionSyntaxRewriter(string deletedRegion) : base(true)
{
this.deletedRegion = deletedRegion;
}
public override SyntaxNode VisitRegionDirectiveTrivia(
RegionDirectiveTriviaSyntax node)
{
currentPosition++;
var regionText = node.ToFullString().Substring(8).Trim();
if (!useRegionNameForAnalysis &&
node.ParentTrivia.Token.Parent is ClassDeclarationSyntax)
{
EndRegionsForDeletion.Add(currentPosition);
return SyntaxFactory.SkippedTokensTrivia();
}
if (useRegionNameForAnalysis &&
regionText == deletedRegion)
{
EndRegionsForDeletion.Add(currentPosition);
return SyntaxFactory.SkippedTokensTrivia();
}
return base.VisitRegionDirectiveTrivia(node);
}
public override SyntaxNode VisitEndRegionDirectiveTrivia(
EndRegionDirectiveTriviaSyntax node)
{
var oldPosition = currentPosition;
currentPosition--;
if (EndRegionsForDeletion.Contains(oldPosition))
{
EndRegionsForDeletion.Remove(currentPosition);
return SyntaxFactory.SkippedTokensTrivia();
}
return base.VisitEndRegionDirectiveTrivia(node);
}
}
答案 1 :(得分:2)
根据Sievajet建议,您可以使用CSharpSyntaxRewriter
删除附加到特定节点的Region
(在您的情况下为ClassDeclarationSyntax
)。
以下是帮助您入门的代码:
public class RegionRemoval : CSharpSyntaxRewriter
{
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
if(node.HasLeadingTrivia)
{
var enumerator = node.GetLeadingTrivia().GetEnumerator();
while(enumerator.MoveNext())
{
var syntaxTrivia = enumerator.Current;
if(syntaxTrivia.Kind().Equals(SyntaxKind.RegionDirectiveTrivia))
{
node = node.ReplaceTrivia(syntaxTrivia, SyntaxFactory.Whitespace("\n"));
}
}
}
return node;
}
}
class RoslynTry
{
public static void RegionRemover()
{
//A syntax tree with an unnecessary semicolon on its own line
var tree = CSharpSyntaxTree.ParseText(@"
#region Classes
public class MyClass
{
}
public class MyClass2
{
#region Methods
#endregion
}
#endregion
");
var rewriter = new RegionRemoval();
var result = rewriter.Visit(tree.GetRoot());
Console.WriteLine(result.ToFullString());
}
}
您的输出应如下所示:
public class MyClass
{
}
public class MyClass2
{
#region Methods
#endregion
}
#endregion
P.S。 :这不是完整的解决方案。我同意mjwills,您应该在发布问题之前先显示一些进展。
P.S。 :代码的灵感来自JoshVarty的EmptyStatementRemoval