向 var 类型的变量添加新项 - 在一个变量中具有构造函数和方法声明

Adding new items to a variable of type var - Having Constructor and Method declarations in one variable

我有以下代码:

var allclasses = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var memcls in allclasses)
{
    if (memcls != null)
    {
        var methodDeclarations = memcls.DescendantNodes().OfType<MethodDeclarationSyntax>();
        foreach (var memmeth in methodDeclarations)
        {
            var paramDeclaratons = memmeth.ParameterList.Parameters;

在定义 methodDeclarations 的行之后,我还想添加一行,如下面的代码:

methodDeclarations.AddRange(
    memcls.DescendantNodes().OfType<ConstructorDeclarationSyntax>())

为了将返回的 ConstructorDeclarationSyntax 项也添加到同一 methodDeclarations 变量;即,在同一个变量中同时包含 ConstructorDeclarationSyntax 和 MethodDeclarationSyntax 项。我怎样才能做到这一点?

我尝试了一些类型转换并使用 List 而不是 var 作为 methodDeclarations 变量,但它在我想访问 ParameterList、Identifier 和 memmeth 上的其他属性的下一行中给出了错误。

作为另一个技巧,我尝试使用以下代码

var methodDeclarations = tree.GetRoot().DescendantNodes()
    .Where(c => c is MethodDeclarationSyntax || c is ConstructorDeclarationSyntax);

但随后在代码后面访问 ParameterList 和 Identifier 属性时再次出现错误。

如@Chris 所述,如果共享基础 class 没有您想要的适当属性,您可能应该使用两个单独的列表。

但是,如果您确实想要使用单个列表,则以下方法可行:

var methodDeclarations =
    tree.GetRoot()
        .DescendantNodes()
        .Where(c => c is MethodDeclarationSyntax || c is ConstructorDeclarationSyntax)
        .Cast<dynamic>();

foreach (var memmeth in methodDeclarations)
{
    // Run-time checking so no syntax error
    Console.WriteLine(memmeth.Identifier);
}

但是请注意,这会降低性能,因为需要反射,并且您还会失去编译器通常会提供的所有良好的静态语法检查。

您可以按如下方式使用 List<BaseMethodDeclarationSyntax>

var methods = new List<BaseMethodDeclarationSyntax>();
methods.AddRange(memcls.DescendantNodes().OfType<MethodDeclarationSyntax>());
methods.AddRange(memcls.DescendantNodes().OfType<ConstructorDeclarationSyntax>());

在该列表中的方法上,您可以使用在 BaseMethodDeclarationSyntax 上声明的所有属性,包括

  • 正文
  • 表达式主体
  • 参数列表
  • ...

但你是对的,那里没有标识符 属性,原因很简单:并非每个 BaseMethodDeclarationSyntax 都有一个。具有标识符 属性 的是 MethodDeclarationSyntax、ConstructorDeclarationSyntax 和 DestructorDeclarationSyntax。没有的是 OperatorDeclarationSyntax 和 ConversionOperatorDeclarationSyntax。

但是,您可以轻松地从那些具有标识符的类型中获取标识符。最有效的方法可能是使用访问者:

internal sealed class IdentifierVisitor : CSharpSyntaxVisitor<SyntaxToken>
{
    public static IdentifierVisitor Instance { get; } = new IdentifierVisitor();

    public override SyntaxToken VisitMethodDeclaration(MethodDeclarationSyntax node)
    => node.Identifier;

    public override SyntaxToken VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
    => node.Identifier;

    public override SyntaxToken VisitDestructorDeclaration(DestructorDeclarationSyntax node)
    => node.Identifier;
}

一旦你有了那个class,你就可以得到标识符如下:

foreach (var method in methods)
{
    var identifier = IdentifierVisitor.Instance.Visit(method);

    // for example:
    Console.WriteLine(identifier.Text);
}

这适用于常规方法、构造函数和析构函数。 在某种程度上,它也适用于运算符和转换运算符,除了那些 return 默认标记。所以你可以这样做:

var methods = memcls
    .DescendantNodes()
    .OfType<BaseMethodDeclarationSyntax>()
    .ToList();

foreach (var method in methods)
{
    var identifier = IdentifierVisitor.Instance.Visit(method);

    if (!identifier.IsKind(SyntaxKind.None))
    {
        Console.WriteLine(identifier.Text);
    }
}

最后一件事:调用 .DescendentNodes() 将为您提供整个 ClassDeclarationSyntax 树中的每个节点。您可能应该改用 .Members 。这不会为您提供嵌套 classes 中的方法(尽管如果您需要它们,您可以轻松地递归地获得这些方法),但它会更有效率。