从 .cs 文件中获取所有变量

Get all variables from .cs file

我想从一个 CS 文件中获取所有变量。 CS 文件将由我的应用程序打开,其代码将被读入字符串。在此之后我想将所有变量(包括名称)放入数组或列表中。之后需要在同一代码中找到这些变量,并将它们的名称替换为 MD5 散列值。 This 文章并没有真正帮助我,因为您无法从 "stringed" 代码中获取变量。我知道,这听起来很奇怪,但我真的需要帮助

这应该可以帮助您开始使用 Roslyn 的语法 API 和 C# 内置分析器。您需要将 "Microsoft.CodeAnalysis.CSharp" nuget 包添加到您的项目中。

假设您有一个源文件,您已将其解析为如下所示的字符串:

public class Person
{
    public string Name { get; set; }
    internal string Age;
    int _val;
    public void DoSomething(string x) { int y = 1; var z = 2; }
}

你可以这样写一些代码:

var parser = new ClassParser(stringOfCode);
var members = parser.GetMembers();
foreach (var m in members)
    Console.WriteLine(m);

要输出这样的文本,向您显示 class 的所有已定义属性和字段的文本和位置(开始和长度),以便您可以根据需要替换名称:

Name: Name (36, 4), Type: string (29, 6), Declaration: public string Name { get; set; } (22, 32)
Name: Age (71, 3), Type: string (64, 6), Declaration: internal string Age; (55, 20)
Name: _val (80, 4), Type: int (76, 3), Declaration: int _val; (76, 9)

为了简化逻辑,您可以先定义一些 classes 来模拟结果并覆盖 ToString() 输出:

public class Member
{
    public string KindText { get; set; }
    public MemberToken NameToken { get; set; }
    public MemberToken TypeToken { get; set; }
    public MemberToken DeclarationToken { get; set; }
    public override string ToString() => $"Name: {this.NameToken}, Type: {this.TypeToken}, Declaration: {this.DeclarationToken}";
}

public class MemberToken
{
    public string Name { get; set; }
    public int Start { get; set; }
    public int Length { get; set; }

    public MemberToken(string code, TextSpan span)
    {
        this.Name = code.Substring(span.Start, span.Length);
        this.Start = span.Start;
        this.Length = span.Length;
    }
    public override string ToString() => $"{this.Name} ({this.Start}, {this.Length})";
}

然后你可以写一个CSharpSyntaxWalker来访问你关心的节点(例如Field和属性声明),并将它们捕获到列表中:

public class MemberCollector : CSharpSyntaxWalker
{ 
    public List<FieldDeclarationSyntax> Fields { get; } = new List<FieldDeclarationSyntax>();
    public List<PropertyDeclarationSyntax> Properties { get; } = new List<PropertyDeclarationSyntax>();
    public List<LocalDeclarationStatementSyntax> Variables { get; } = new List<LocalDeclarationStatementSyntax>();

    public override void VisitFieldDeclaration(FieldDeclarationSyntax node) => this.Fields.Add(node);
    public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) => this.Properties.Add(node);
    public override void VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node) => this.Variables.Add(node);
}

最后,你只需要编写你的class解析器来初始化语法树,然后分析语法树来识别成员。

public class ClassParser
{
    public string Code { get; set; }
    public SyntaxNode Root { get; set; }

    public ClassParser(string code)
    {
        this.Code = code;
        var tree = CSharpSyntaxTree.ParseText(code);
        this.Root = tree.GetCompilationUnitRoot();
    }

    public List<Member> GetMembers()
    {
        var collector = new MemberCollector();
        collector.Visit(this.Root);
        var results = new List<Member>();
        results.AddRange(collector.Properties.Select(p => new Member()
        {
            KindText = p.Kind().ToString(),
            DeclarationToken = new MemberToken(this.Code, p.Span),
            NameToken = new MemberToken(this.Code, p.Identifier.Span),
            TypeToken = new MemberToken(this.Code, p.Type.Span),
        }));
        results.AddRange(collector.Fields.SelectMany(f => f.Declaration.Variables.Select(v => new Member()
        {
            KindText = f.Kind().ToString(),
            DeclarationToken = new MemberToken(this.Code, f.Span),
            TypeToken = new MemberToken(this.Code, f.Declaration.Type.Span),
            NameToken = new MemberToken(this.Code, v.Span),
        })));
        results.AddRange(collector.Variables.SelectMany(f => f.Declaration.Variables.Select(v => new Member()
        {
            KindText = f.Kind().ToString(),
            DeclarationToken = new MemberToken(this.Code, f.Span),
            TypeToken = new MemberToken(this.Code, f.Declaration.Type.Span),
            NameToken = new MemberToken(this.Code, v.Span),
        })));
        return results;
    }
}