使用Roslyn向C#类添加自定义属性

本文关键字:添加 自定义属性 Roslyn 使用 | 更新日期: 2023-09-27 18:23:55

考虑文件"MyClass.cs"中的以下类

using System;
public class MyClass : Entity<long>
{
    public long Id
    {
        get;
        set;
    }
    [Required]
    public string Name
    {
        get;
        set;
    }
    public string Slug
    {
        get;
        set;
    }
    public DateTime CreatedOn
    {
        get;
        private set;
    }
    public DateTime UpdatedOn
    {
        get;
        private set;
    }
    /* ... */
}

目前,我手动创建数据契约类,如下所示:

[DataContract(Namespace = "http://example.com/", Name = "MyClass")]
public sealed class MyClass
{
    [DataMember(EmitDefaultValue = false, Name = "Id")]
    public long Id
    {
        get;
        set;
    }
    [DataMember(EmitDefaultValue = false, Name = "Name", IsRequired = true)]
    public string Name
    {
        get;
        set;
    }
    [DataMember(EmitDefaultValue = false, Name = "Slug")]
    public string Slug
    {
        get;
        set;
    }
    [DataMember(EmitDefaultValue = false, Name = "CreatedOn")]
    public DateTime CreatedOn
    {
        get;
        set;
    }
    [DataMember(EmitDefaultValue = false, Name = "UpdatedOn")]
    public DateTime UpdatedOn
    {
        get;
        set;
    }
}

我想用Roslyn重写"MyClass.cs",这样它看起来就像我手工创建的类。目前我有以下几种:

using System;
using System.IO;
using Roslyn.Compilers.CSharp;
internal class Program
{
    private static void Main()
    {
        var reader = new StreamReader(@"..'..'MyClass.cs");
        var source = reader.ReadToEnd();
        var tree = SyntaxTree.ParseCompilationUnit(source);
        var rewriter = new MyRewriter();
        var newRoot = rewriter.Visit(tree.Root);
        Console.WriteLine(newRoot.Format());
    }
}
public class MyRewriter : SyntaxRewriter
{
    protected override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
    {
        var declaration = (TypeDeclarationSyntax) base.VisitClassDeclaration(node);
        return ((ClassDeclarationSyntax) declaration).Update(
            declaration.Attributes,
            Syntax.TokenList(Syntax.Token(SyntaxKind.PublicKeyword), Syntax.Token(SyntaxKind.SealedKeyword)),
            declaration.Keyword,
            declaration.Identifier,
            declaration.TypeParameterListOpt,
            null,
            declaration.ConstraintClauses,
            declaration.OpenBraceToken,
            declaration.Members,
            declaration.CloseBraceToken,
            declaration.SemicolonTokenOpt);
    }
    protected override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
    {
        var typeSyntax = node.Type;
        if (node.Identifier.ValueText == "Id")
        {
            typeSyntax = Syntax.IdentifierName("string");
        }
        var newProperty = Syntax.PropertyDeclaration(
            modifiers: Syntax.TokenList(Syntax.Token(SyntaxKind.PublicKeyword)),
            type: typeSyntax,
            identifier: node.Identifier,
            accessorList: Syntax.AccessorList(
                accessors: Syntax.List(
                    Syntax.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, 
                    semicolonTokenOpt: Syntax.Token(SyntaxKind.SemicolonToken)),
                    Syntax.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration,
                    semicolonTokenOpt: Syntax.Token(SyntaxKind.SemicolonToken))
                    )
                )
            );
        return newProperty;
    }
}

我一直试图找到一种将DataMember和DataContract自定义属性添加到MyClass的方法,但没有成功。如何添加自定义属性?

使用Roslyn向C#类添加自定义属性

Syntax.PropertyDeclaration方法的参数之一是应用于该属性的属性列表。与所有Syntax元素一样,它是使用静态SyntaxFactory类上的工厂方法构造的。

Roslyn Quoter可以方便地了解如何使用Roslyn生成语法。

在您的特定示例中,重写器的VisitPropertyDeclaration方法应该类似于:

using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
...
    protected override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
{
    var typeSyntax = node.Type;
    if (node.Identifier.ValueText == "Id")
    {
        typeSyntax = SyntaxFactory.IdentifierName("string");
    }
    var newProperty = PropertyDeclaration(
                PredefinedType(
                    Token(SyntaxKind.LongKeyword)),
                Identifier("Id"))
            .WithModifiers(
                TokenList(
                    Token(SyntaxKind.PublicKeyword)))
            .WithAccessorList(
                AccessorList(
                    List(new AccessorDeclarationSyntax[]{
                        AccessorDeclaration(
                            SyntaxKind.GetAccessorDeclaration)
                        .WithSemicolonToken(
                            Token(SyntaxKind.SemicolonToken)),
                        AccessorDeclaration(
                            SyntaxKind.SetAccessorDeclaration)
                        .WithSemicolonToken(
                            Token(SyntaxKind.SemicolonToken))})))
            .NormalizeWhitespace();
    return newProperty;
}