如何使用Roslyn生成类字段的初始化

时间:2015-12-30 15:40:02

标签: c# roslyn

我知道如何在方法中创建局部变量,例如:

LocalDeclarationStatement(VariableDeclaration(IdentifierName("MyClass"))
            .WithVariables(SingletonSeparatedList(VariableDeclarator(Identifier("nameOfvariable"))
                .WithInitializer(
                    EqualsValueClause(
                        ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
                            .WithNewKeyword(Token(SyntaxKind.NewKeyword)))))));

会给我:

MyClass nameOfvariable = new MyClass();

但是说我已经创建了一个字段,现在我只想初始化它(在方法,构造函数或任何东西中),如下所示:

nameOfVariable = new MyClass();

我该怎么做?我的猜测它与VariableDeclerator有关,但是我找不到一种正确的方法,所以我可以将它添加到包含StatementSyntaxes的列表中。我也可以将VariableDecleration更改为“VariableDeclaration(IdentifierName(”“))”,但这给了我一个丑陋的额外空间。

似乎我在与Roslyn的一些非常基本的东西挣扎,我试图检查http://roslynquoter.azurewebsites.net/,但这感觉就像是强制执行它的方式(感觉它创建的代码多于必要的代码)。

更新:应该澄清我知道如何创建方法/构造函数。当我只能访问字段名称和字段类型时,我只想找到一种初始化字段的方法。所以我想要生成的唯一代码是:

myField = new MyField();

3 个答案:

答案 0 :(得分:4)

嗯,你几乎就在那里,你只需要创造所有这些。这应该做你感兴趣的事情:

const string source = @"
using System;

class MyClass
{
    void Method()
    {
        MyClass nameOfVariable;
    }
}
";
var tree = CSharpSyntaxTree.ParseText(source);
var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();

var local = root.DescendantNodes().OfType<LocalDeclarationStatementSyntax>().First();
var declaration = local.Declaration;
var declarator = declaration.Variables.First();

var identifier = SyntaxFactory.IdentifierName("MyClass");
var objectCreationExpression = SyntaxFactory.ObjectCreationExpression(identifier, SyntaxFactory.ArgumentList(), null);
var equalsValueClause = SyntaxFactory.EqualsValueClause(objectCreationExpression);
var newDeclarator = declarator.WithInitializer(equalsValueClause).WithAdditionalAnnotations(Formatter.Annotation);
var newRoot = root.ReplaceNode(declarator, newDeclarator);
var formattedRoot = Formatter.Format(newRoot, Formatter.Annotation, new AdhocWorkspace());

Console.WriteLine(formattedRoot.GetText());
Console.Read();

一些解释:您创建了一个新标识MyClass,将在ObjectCreationExpression中使用。然后将所有内容包装在EqualsValueClause中,并将其设置为声明符的初始值设定项。我们还将Formatter注释添加到此节点,以便我们以后可以对其进行格式化,并且不会出现空白问题。

然后剩下的就是替换原始树中的节点,格式化它并且你已经完成了:

enter image description here

----------------------------------------------- --------------------------------

如果您想要将赋值与声明分开放置,那么您必须创建一个新的AssignmentExpression并将其包装在ExpressionStatement内。通常,表达式和语句是不同的概念,但是这个ExpressionStatement允许我们将表达式视为一个重要的语句,因为方法的主体只接受语句。

在代码中,它看起来像这样:

internal static void Execute()
{
    const string source = @"
using System;

class MyClass
{
    void Method()
    {
        MyClass nameOfVariable, another;
    }
}
";
    var tree = CSharpSyntaxTree.ParseText(source);
    var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
    var semanticModel = compilation.GetSemanticModel(tree);
    var root = tree.GetRoot();

    var local = root.DescendantNodes().OfType<LocalDeclarationStatementSyntax>().First();
    var method = local.Ancestors().OfType<MethodDeclarationSyntax>().First();

    var variableIdentifier = SyntaxFactory.IdentifierName("nameOfVariable");
    var classIdentifier = SyntaxFactory.IdentifierName("MyClass");
    var objectCreationExpression = SyntaxFactory.ObjectCreationExpression(classIdentifier, SyntaxFactory.ArgumentList(), null);
    var assignment = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, variableIdentifier, objectCreationExpression);
    var expressionStatement = SyntaxFactory.ExpressionStatement(assignment).WithAdditionalAnnotations(Formatter.Annotation);
    var newMethod = method.AddBodyStatements(expressionStatement);

    var newRoot = root.ReplaceNode(method.Body, newMethod.Body);
    var formattedRoot = Formatter.Format(newRoot, Formatter.Annotation, new AdhocWorkspace());

    Console.WriteLine(formattedRoot.GetText());
    Console.Read();
}

结果:

enter image description here

答案 1 :(得分:1)

经过一些尝试和寻找后,我找到了答案。你可以使用一种叫做“AssignmentExpression”的东西。

以下是如何使用它的示例:

ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("myField"),
            ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
                .WithNewKeyword(Token(SyntaxKind.NewKeyword))));

这会给你:

myField = new Myclass(); 

所以现在很容易将创建和赋值/初始化分成两个不同的语句。

请注意,我正在使用“使用静态Microsoft.CodeAnalysis.CSharp.SyntaxFactory;”所以我不必一直写SyntaxFactory。

答案 2 :(得分:0)

或者您可以转到“ http://roslynquoter.azurewebsites.net/”并将代码粘贴到小的文本框中,然后单击“获取Roslyn API调用以生成此代码”。

(我可以生成您上面发布的代码,但这有点长。所以我使用一个简单的示例。

例如,假设您粘贴“ DateTime mydate2 = new DateTime()”,该工具将生成以下代码:-

LocalDeclarationStatement(
VariableDeclaration(
    IdentifierName("DateTime"))
.WithVariables(
    SingletonSeparatedList<VariableDeclaratorSyntax>(
        VariableDeclarator(
            Identifier("mydate2"))
        .WithInitializer(
            EqualsValueClause(
                ObjectCreationExpression(
                    IdentifierName("DateTime"))
                .WithArgumentList(
                    ArgumentList())))))).WithSemicolonToken(
MissingToken(SyntaxKind.SemicolonToken)).NormalizeWhitespace()

然后,您只需要使用SyntaxFactory来修复代码,例如:-

   var myDeclaratyion = SyntaxFactory.LocalDeclarationStatement(
SyntaxFactory.VariableDeclaration(
    SyntaxFactory.IdentifierName("DateTime")).
WithVariables(
    SyntaxFactory.SingletonSeparatedList<VariableDeclaratorSyntax>(
        SyntaxFactory.VariableDeclarator(
            SyntaxFactory.Identifier("mydate2")).
        WithInitializer(
            SyntaxFactory.EqualsValueClause(
                SyntaxFactory.ObjectCreationExpression(
                    SyntaxFactory.IdentifierName("DateTime"))
                .WithArgumentList(
                    SyntaxFactory.ArgumentList())))))).WithSemicolonToken(SyntaxFactory.MissingToken(SyntaxKind.SemicolonToken)).NormalizeWhitespace();