2014-07-17 55 views
9

我需要解析用户编写的C#代码片段,并用方法调用替换本地未定义的所有变量。即用方法替换C#代码中的所有变量

public class Foo 
{ 
    public dynamic Bar() 
    { 
    return Math.Min(x + width, maxWidth); 
    } 
} 

有可能成为:

public class Foo 
{ 
    public dynamic Bar() 
    { 
     return Math.Min(Resolve("x") + Resolve("width"), Resolve("maxWidth")); 
    } 
} 

我使用Microsoft.CodeAnalysis.CSharp和CSharpSyntaxTree检查字符串,但它并没有给我足够的信息来进行替换。或者如果是这样,我不知道在哪里寻找它。我粘贴了下面的SyntaxTree布局。所有变量都作为IdentifierName节点出现,但我不知道如何区分不同的IdentifierNames。然后去哪儿?

CompilationUnit[0..99) { 
code: public class Foo\n{\n public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n} 
tokens: EndOfFileToken[] 
nodes{ 
    ClassDeclaration[0..99) { 
    code: public class Foo\n{\n public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n} 
    tokens: PublicKeyword[public ] ClassKeyword[class ] IdentifierToken[Foo\n] OpenBraceToken[{\n] CloseBraceToken[}] 
    nodes{ 
    MethodDeclaration[21..98) { 
    code: public dynamic Bar()\n {\n return Math.Min(x + width, maxWidth);\n }\n 
    tokens: PublicKeyword[ public ] IdentifierToken[Bar] 
    nodes{ 
     IdentifierName[30..38) { 
     code: dynamic 
     tokens: IdentifierToken[dynamic ] 
     } 
     ParameterList[41..45) { 
     code: ()\n 
     tokens: OpenParenToken[(] CloseParenToken[)\n] 
     } 
     Block[45..98) { 
     code: {\n return Math.Min(x + width, maxWidth);\n }\n 
     tokens: OpenBraceToken[ {\n] CloseBraceToken[ }\n] 
     nodes{ 
     ReturnStatement[50..93) { 
     code:  return Math.Min(x + width, maxWidth);\n 
     tokens: ReturnKeyword[ return ] SemicolonToken[;\n] 
     nodes{ 
      InvocationExpression[61..90) { 
      code: Math.Min(x + width, maxWidth) 
      nodes{ 
      SimpleMemberAccessExpression[61..69) { 
      code: Math.Min 
      tokens: DotToken[.] 
      nodes{ 
       IdentifierName[61..65) { 
       code: Math 
       tokens: IdentifierToken[Math] 
       } 
       IdentifierName[66..69) { 
       code: Min 
       tokens: IdentifierToken[Min] 
       } 
      } 
      } 
      ArgumentList[69..90) { 
      code: (x + width, maxWidth) 
      tokens: OpenParenToken[(] CommaToken[, ] CloseParenToken[)] 
      nodes{ 
       Argument[70..79) { 
       code: x + width 
       nodes{ 
       AddExpression[70..79) { 
       code: x + width 
       tokens: PlusToken[+ ] 
       nodes{ 
        IdentifierName[70..72) { 
        code: x 
        tokens: IdentifierToken[x ] 
        } 
        IdentifierName[74..79) { 
        code: width 
        tokens: IdentifierToken[width] 
        } 
       } 
       } 
       } 
       } 
       Argument[81..89) { 
       code: maxWidth 
       nodes{ 
       IdentifierName[81..89) { 
       code: maxWidth 
       tokens: IdentifierToken[maxWidth] 
       } 
       } 
       } 
      } 
      } 
      } 
      } 
     } 
     } 
     } 
     } 
    } 
    } 
    } 
    } 
} 
} 
+1

语法树不够,您需要查看语义模型以查看标识符代表的内容。 –

+0

你能提供一个链接,我可以去阅读语义模型吗? –

+0

现在正在寻找...我有一段时间没有看过Roslyn,并且自上次以来API已经发生了很大的变化 –

回答

7

我认为你需要使用语义模型。这里有一个(非常基本的)示例,说明如何找到未解决的符号:

var tree = CSharpSyntaxTree.ParseFile(fileName); 
var root = tree.GetRoot(); 
var refs = new MetadataReference[] 
{ 
    new MetadataFileReference(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll", new MetadataReferenceProperties(MetadataImageKind.Assembly)) 
}; 
var compilation = CSharpCompilation.Create("testRoslyn", new[] { tree }, refs); 
var model = compilation.GetSemanticModel(tree); 

var unknownSymbols = 
    from node in root.DescendantNodes() 
    where node.IsKind(SyntaxKind.IdentifierName) 
    let symbolInfo = model.GetSymbolInfo(node) 
    where symbolInfo.Symbol == null && !symbolInfo.CandidateSymbols.Any() 
    select node; 

从那里,你可以用Resolve(name)更换节点。

+0

那么,这一切都工作得很好,我仍然完全坚持如何替换节点。有一些关于Microsoft.CodeAnalysis背后的逻辑,我只是​​还没有得到... –

+1

你有没有读过FAQ?他们讨论替换那里的子表达式:https://roslyn.codeplex.com/wikipage?title=FAQ&referringTitle=Documentation – JoshVarty

+0

我似乎无法下载应该包含示例的SDK预览版。该链接将我带到https://connect.microsoft.com/VisualStudio,而这一切都是从那里开始的。我在电脑上搜索了faq.cs和其他所有我能想到的东西,但没有喜悦。 –

1

也许你不经意地粘贴CSharpSyntaxTree而不声明“double width = 10.0;”? 如果是这样,您将在CSharpSyntaxTree中获得这些额外的声明。

您只需扫描未在用户代码中声明的IdentifierToken的树,所有这些标记都具有必须使用的位置,以将变量acces代码替换为方法调用代码。

+0

我错误地更改了代码,以后显示都定义了一个未定义的变量,忘记了代码树会变得无效。我改回来了。 –

2

本文并不真正为您解决问题,而是另一种可能更容易实施的方式,但为用户引入更改。我只是在这里发表一个想法。

的目的不是为了让用户编写x而是Var.xVar.maxWidth

然后,解析您的C#代码时,你只需要为CustomDynamicObject类型的属性Var(或你的任何名称插入代码想给)

public (static?) CustomDynamicObject Var { get { /* create the object once and return */ }} 

然后你就可以定义CustomDynamicObject继承DynamicObject,这样就可以拦截到未定义的方法/属性

所有呼叫0

DynamicObject和使用.NET 4的动态特性只是拦截调用的一种方式,但您可以使用谷歌的其他技术。

+0

如果我能帮到的话,我宁愿不对用户承担任何责任。这是一个有趣的想法,但最初我想解决它,而不需要用户代码与常规C#代码不同。 –

+0

是的,这就是为什么我发布它“作为一个想法”。如果您没有找到解决问题的办法,那可能是一个备份解决方案。但你完全正确,用户必须承受最小的压力。 – Fabske