2016-02-27 89 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"; 
      } 
     } 
    }