2016-02-27 65 views
4

我有一些簡化示例代碼,希望我試圖完成。如何使用Roslyn獲取編譯時間常量值

OptionAtrribute.cs

using System; 
public class OptionAttribute : Attribute 
{ 
    public OptionAttribute(string option) 
    { 
     this.PickedOption = option; 
    } 

    public string PickedOption { get; private set; } 
} 

Options.cs

using System; 
public class Options 
{ 
    public const string Cat = "CT"; 
    public const string Dog = "DG"; 
    public const string Monkey = "MNKY"; 
} 

SomeClass.cs

using System; 
[Option(Options.Dog)] 
public class SomeClass 
{ 

} 

哪有我在「SomeClass」類上獲得「OptionAttribute」並獲得「PickedOption」值?

更新

我不問如何使用反射。這是用於在保存代碼的文件保存時生成代碼。我沒有一個更新的DLL在這一點上,所以反射將無法正常工作我正在嘗試使用Roslyn來解析實際的文件。 下面是我不得不嘗試

string solutionPath = @"C:\Project\Project.sln"; 
var msWorkspace = MSBuildWorkspace.Create(); 

var solution = await msWorkspace.OpenSolutionAsync(solutionPath); 
var project = solution.Projects.FirstOrDefault(p => p.Name == "Project1"); 
var compilation = await project.GetCompilationAsync(); 

var document = project.Documents.FirstOrDefault(d => d.Name == "Code.cs"); 

SyntaxTree syntaxTree = null; 
document.TryGetSyntaxTree(out syntaxTree); 
var semanticModel = compilation.GetSemanticModel(syntaxTree); 

var commandCategoryAttribute = compilation.GetTypeByMetadataName("Project1.OptionAttribute"); 
var classDeclaration = syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().Skip(2).FirstOrDefault(); 
var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); 
var attrSymbol = classSymbol.GetAttributes().FirstOrDefault(a => a.AttributeClass.MetadataName == commandCategoryAttribute.MetadataName);  
var attrSyntax = classDeclaration.AttributeLists.FirstOrDefault().Attributes.FirstOrDefault(); 

我的解決方案

我得到它的工作!

public void Test() 
{ 
    string solutionPath = @"C:\Project\Project.sln"; 
    var msWorkspace = MSBuildWorkspace.Create(); 
    var solution = msWorkspace.OpenSolutionAsync(solutionPath).Result; 
    var project = solution.Projects.FirstOrDefault(p => p.Name == "Project1"); 
    var compilation = project.GetCompilationAsync().Result; 

    var document = project.Documents.FirstOrDefault(d => d.Name == "SomeClass.cs"); 
    SyntaxTree syntaxTree = null; 
    document.TryGetSyntaxTree(out syntaxTree); 
    SemanticModel semanticModel = compilation.GetSemanticModel(syntaxTree); 


    ClassDeclarationSyntax classDeclaration = syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault(); 
    var attr = classDeclaration.AttributeLists.SelectMany(a => a.Attributes).FirstOrDefault(a => a.Name.ToString() == "Option"); 


    var exp = attr.ArgumentList.Arguments.First().Expression; 
    string value = null; 
    var mem = exp as MemberAccessExpressionSyntax; 
    if (mem != null) 
    { 
     value = ResolveMemberAccess(mem, solution, compilation)?.ToString(); 
    } 
    else 
    { 
     var lit = exp as LiteralExpressionSyntax; 
     if (lit != null) 
     { 
      value = semanticModel.GetConstantValue(lit).Value?.ToString(); 
     } 
    } 
} 
public object ResolveMemberAccess(MemberAccessExpressionSyntax memberSyntax, Solution solution, Compilation compilation) 
{ 
    var model = compilation.GetSemanticModel(memberSyntax.SyntaxTree); 
    var memberSymbol = model.GetSymbolInfo(memberSyntax).Symbol; 
    var refs = SymbolFinder.FindReferencesAsync(memberSymbol, solution).Result.FirstOrDefault(); 

    if (refs != null) 
    { 
     var defSyntax = refs.Definition.DeclaringSyntaxReferences.First(); 
     var parent = compilation.GetSemanticModel(defSyntax.SyntaxTree); 
     var syn = defSyntax.GetSyntax(); 

     var literal = syn.DescendantNodes().OfType<LiteralExpressionSyntax>().FirstOrDefault(); 
     if (literal != null) 
     { 
      var val = parent.GetConstantValue(literal); 
      return val.Value; 
     } 
     else 
     { 
      var memberAccess = syn.DescendantNodes().OfType<MemberAccessExpressionSyntax>().FirstOrDefault(); 
      if (memberAccess != null) 
      { 
       return ResolveMemberAccess(memberAccess, solution, compilation); 
      } 
     } 
    } 
    return null; 
} 

謝謝!

+1

的可能的複製[?我如何讀取在運行時類的屬性(http://stackoverflow.com/questions/2656189/how-do-i-read-an-attribute-on-a-class-at-runtime) –

回答

2

聽起來對我來說就好像你真的很接近。考慮加入這代碼:

attrSyntax.ArgumentList.Arguments.First().Expression.ToString() 

這將整齊地返回

Options.Dog

那麼你知道你有這些信息可用。如果你改變它一點點例如這樣的:

var expression = attrSyntax.ArgumentList.Arguments.First().Expression as MemberAccessExpressionSyntax; 
expression.Name.Identifier.ValueText.Dump(); 

你得到的輸出

但是,如果你想實際值它指向你可以做到這一點:

var x = classDeclaration.AttributeLists.First().Attributes.First().ArgumentList.Arguments.First(); 
semanticModel.GetConstantValue(x.Expression).Value.Dump(); 

這將輸出

DG於VS2017預期

+0

感謝您的幫助,但如果「Options」類和「SomeClass」類不在同一個語法樹中? –

+2

您從Compilation獲得的語義模型將查找編譯中所有語法樹的內容。把編譯看作是把一堆不同的樹連在一起的「膠水」。 –

2

SemanticModel.GetConstantValue()不起作用。

下面是處理一個簡單的字符串聲明的例子:

private string GetDllName(AttributeSyntax importAttribute, Compilation compilation) 
    { 
     var expression = importAttribute.ArgumentList.Arguments[0].Expression; 
     return GetConstantValue(expression, compilation); 
    } 

    private string GetConstantValue(ExpressionSyntax expression, Compilation compilation) 
    { 
     if (expression.IsKind(SyntaxKind.StringLiteralExpression)) 
     { 
      var literal = expression as LiteralExpressionSyntax; 
      return literal.Token.ValueText; 
     } 
     else if (expression.IsKind(SyntaxKind.AddExpression)) 
     { 
      var binaryExpression = expression as BinaryExpressionSyntax; 
      return GetConstantValue(binaryExpression.Left, compilation) + 
       GetConstantValue(binaryExpression.Right, compilation); 
     } 
     else 
     { 
      var model = compilation.GetSemanticModel(expression.SyntaxTree); 
      var symbol = model.GetSymbolInfo(expression).Symbol; 
      var defNode = symbol.DeclaringSyntaxReferences.First().GetSyntax(); 

      var valueClause = defNode.DescendantNodes().OfType<EqualsValueClauseSyntax>().FirstOrDefault(); 
      if (valueClause != null) 
      { 
       return GetConstantValue(valueClause.Value, compilation); 
      } 
      else 
      { 
       return "Unknown"; 
      } 
     } 
    }