2012-02-09 22 views
10

Considérese la siguiente clase en un archivo "MyClass.cs"Adición de atributos personalizados a clases de C# con Roslyn

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; 
    } 

    /* ... */ 
} 

Actualmente me manualmente crear datos de clases de contratos en busca de la siguiente manera:

[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; 
    } 
} 

I' Me gusta usar Roslyn para reescribir "MyClass.cs", por lo que se parece a la clase que creo a mano. Actualmente tengo el siguiente:

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; 
    } 
} 

He estado tratando de encontrar una manera de agregar los atributos de DataMember y DataContract costumbre de MiClase, pero no han tenido éxito. ¿Cómo se pueden agregar los atributos personalizados?

+0

La forma en que leí [esto] (http://social.msdn.microsoft.com/Forums/en- US/roslyn/thread/f5adeaf0-49d0-42dc-861b-0f6ffd731825) MSDN thread, yo diría que Roslyn no admite atributos personalizados. ¿Has visto algo que indique lo contrario? –

+0

Roslyn admite atributos en el nivel de sintaxis, pero no en la semántica –

+0

Hay una clase AttributeDeclarationSyntax y tanto la clase como las propiedades tienen un miembro de atributos, pero no encuentro un ejemplo de cómo construirlo. También puede reescribir los atributos con un SyntaxRewiter. Aquí hay un [ejemplo] (http://www.mindscapehq.com/blog/index.php/2011/10/20/in-bed-with-roslyn/) cómo usarlo. Entonces creo que es compatible, pero puedo estar equivocado. – bloudraak

Respuesta

10

Uno de los parámetros del método Syntax.PropertyDeclaration es una lista de atributos que se aplican al atributo. Como todos los elementos Syntax, se construye usando un método de fábrica en la clase estática SyntaxFactory.

El Roslyn Quoter puede ser útil para descubrir cómo generar sintaxis usando Roslyn.

En el ejemplo particular, el método VisitPropertyDeclaration de su re-escritura debe ser algo como:

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; 
}  
+0

Eso lo noté, pero no pude encontrar la forma de inicializarlo, de ahí la pregunta. – bloudraak

+0

Ver respuesta actualizada. –

+0

Justo lo que estaba buscando. Gracias. – bloudraak

Cuestiones relacionadas