如何使用 Roslyn 生成 class 字段的初始化
How to generate initialization of class fields with 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(""))" 但这给了我一个丑陋的额外 space 语句。
我似乎在与 Roslyn 的一些非常基本的东西作斗争,我尝试检查 http://roslynquoter.azurewebsites.net/ 但这感觉像是被迫的方式(感觉它创建了比必要更多的代码)。
更新:应该说明我知道如何创建 method/constructors。当我只能访问字段名称和字段类型时,我只是在寻找一种初始化字段的方法。所以我想生成的唯一代码是:
myField = new MyField();
好吧,你就快完成了,你只需要创建所有这些。这应该可以满足您的兴趣:
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
注释,以便稍后对其进行格式化,不会出现空白问题。
剩下的就是替换原始树中的节点,对其进行格式化,然后就完成了:
-------------------------------------------- ----------------------------------
如果您的意思是要将赋值与声明分开,那么您必须创建一个新的 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();
}
结果:
经过更多的尝试和寻找,我找到了答案。您可以使用名为 "AssignmentExpression" 的东西。
这是一个如何使用它的例子:
ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("myField"),
ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
.WithNewKeyword(Token(SyntaxKind.NewKeyword))));
这会给你:
myField = new Myclass();
所以现在很容易将创建和 assignment/initialization 分开为两个不同的语句。
请注意,我正在使用 "using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;",因此我不必一直编写 SyntaxFactory。
或者您可以转到“http://roslynquoter.azurewebsites.net/”并将您的代码粘贴到小文本框中,然后单击 "Get Roslyn API calls to generate this code"。
(我可以生成你在上面发布的代码,但它有点长。所以我使用一个简单的例子。
例如,假设您粘贴 "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();
我知道如何在方法中创建局部变量,例如:
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(""))" 但这给了我一个丑陋的额外 space 语句。
我似乎在与 Roslyn 的一些非常基本的东西作斗争,我尝试检查 http://roslynquoter.azurewebsites.net/ 但这感觉像是被迫的方式(感觉它创建了比必要更多的代码)。
更新:应该说明我知道如何创建 method/constructors。当我只能访问字段名称和字段类型时,我只是在寻找一种初始化字段的方法。所以我想生成的唯一代码是:
myField = new MyField();
好吧,你就快完成了,你只需要创建所有这些。这应该可以满足您的兴趣:
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
注释,以便稍后对其进行格式化,不会出现空白问题。
剩下的就是替换原始树中的节点,对其进行格式化,然后就完成了:
-------------------------------------------- ----------------------------------
如果您的意思是要将赋值与声明分开,那么您必须创建一个新的 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();
}
结果:
经过更多的尝试和寻找,我找到了答案。您可以使用名为 "AssignmentExpression" 的东西。
这是一个如何使用它的例子:
ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, IdentifierName("myField"),
ObjectCreationExpression(IdentifierName("MyClass")).WithArgumentList(arguments)
.WithNewKeyword(Token(SyntaxKind.NewKeyword))));
这会给你:
myField = new Myclass();
所以现在很容易将创建和 assignment/initialization 分开为两个不同的语句。
请注意,我正在使用 "using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;",因此我不必一直编写 SyntaxFactory。
或者您可以转到“http://roslynquoter.azurewebsites.net/”并将您的代码粘贴到小文本框中,然后单击 "Get Roslyn API calls to generate this code"。
(我可以生成你在上面发布的代码,但它有点长。所以我使用一个简单的例子。
例如,假设您粘贴 "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();