当前位置:网站首页>Source generator actual combat
Source generator actual combat
2022-04-23 03:06:00 【Dotnet cross platform】
Preface
Last brush B I browsed Lao Yang's about Source Generator Introduction video of . In fact, at the beginning .Net 6 I saw this thing introduced by Microsoft when it was just released , But I didn't care . Because I think there are many restrictions on this thing , After all C# It's a strongly typed language , Some dynamic things are not easy to operate , And there are Fody、Natasha These operations IL The library of .
Recently, there are more front-end writing , See this and this , Are automatically introduced into related packages , It has greatly improved the comfort of the front-end I developed . Think of the next door Java There are Lombok, It smells good to use . I searched it and didn't see C# There are related things , So I decided to develop a , Improve C# Development experience .
Achieve one Source Generator
Here is wrong Source Generator Make a basic introduction , Direct practice . If you need relevant information , It is recommended to directly read the official documents or search for relevant articles .
First, let's look at the effect , If my code is
namespace SourceGenerator.Demo
{
public partial class UserClass
{
[Property]
private string _test;
}
}
that , The final generation should be
// Auto-generated code
namespace SourceGenerator.Demo
{
public partial class UserClass
{
public string Test { get => _test; set => _test = value; }
}
}
Let's consider the simplest implementation , So just
Find... In the syntax tree field
Find the of the field class、namespace
The generated code
First step
First, let's look at the first step . The first step is to find field, We can do this with the help of Attribute Characteristics of , Can quickly find , stay SourceGenerator Just judge Attribute The name of
Define a SyntaxReciver, And then in SourceGenerator Sign up for
// file: PropertyAttribute.cs
using System;
namespace SourceGenerator.Common
{
[AttributeUsage(AttributeTargets.Field)]
public class PropertyAttribute : Attribute
{
public const string Name = "Property";
}
}
// file: AutoPropertyReceiver.cs
public class AutoPropertyReceiver : ISyntaxReceiver
{
public List<AttributeSyntax> AttributeSyntaxList { get; } = new List<AttributeSyntax>();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is AttributeSyntax cds && cds.Name is IdentifierNameSyntax identifierName &&
(
identifierName.Identifier.ValueText == PropertyAttribute.Name ||
identifierName.Identifier.ValueText == nameof(PropertyAttribute))
)
{
AttributeSyntaxList.Add(cds);
}
}
}
// file: AutoPropertyGenerator.cs
[Generator]
public class AutoPropertyGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new AutoPropertyReceiver());
}
// other code
...
}
The second step
The second step is SyntaxTree Lookup , be familiar with SyncaxTree It's easier to finish
public void Execute(GeneratorExecutionContext context)
{
var syntaxReceiver = (AutoPropertyReceiver)context.SyntaxReceiver;
var attributeSyntaxList = syntaxReceiver.AttributeSyntaxList;
if (attributeSyntaxList.Count == 0)
{
return;
}
// Save the class name , Because there may be multiple fields in a class to generate , Remove the repetition here
var classList = new List<string>();
foreach (var attributeSyntax in attributeSyntaxList)
{
// find class, And judge whether there is parital Field
var classDeclarationSyntax = attributeSyntax.FirstAncestorOrSelf<ClassDeclarationSyntax>();
if (classDeclarationSyntax == null ||
!classDeclarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
{
continue;
}
// find namespace
var namespaceDeclarationSyntax =
classDeclarationSyntax.FirstAncestorOrSelf<BaseNamespaceDeclarationSyntax>();
if (classList.Contains(classDeclarationSyntax.Identifier.ValueText))
{
continue;
}
// find field
var fieldDeclarationList = classDeclarationSyntax.Members.OfType<FieldDeclarationSyntax>().ToList();
if (fieldDeclarationList.Count == 0)
{
continue;
}
// Other code
...
}
}
The third step
The third step is simply and roughly based on the information obtained in the second step , Spell the string .
Of course, string spelling is a very bad behavior , It's best to use templates to achieve , Secondly, even if it is a string spell, it can also be applied StringBuilder, But this is just a Demo, Doesn't matter
public void Execute(GeneratorExecutionContext context)
{
...
// Above is the code of the second step
// Spell the source code string
var source = $@"// Auto-generated code
namespace {namespaceDeclarationSyntax.Name.ToString()}
{
{
public partial class {classDeclarationSyntax.Identifier}
{
{";
var propertyStr = "";
foreach (var fieldDeclaration in fieldDeclarationList)
{
var variableDeclaratorSyntax = fieldDeclaration.Declaration.Variables.FirstOrDefault();
var fieldName = variableDeclaratorSyntax.Identifier.ValueText;
var propertyName = GetCamelCase(fieldName);
propertyStr += $@"
public string {propertyName} {
{ get => {fieldName}; set => {fieldName} = value; }}";
}
source += propertyStr;
source += @"
}
}
";
// Add to source code , such IDE To perceive
context.AddSource($"{classDeclarationSyntax.Identifier}.g.cs", source);
// Save the class name , Avoid duplicate generation
classList.Add(classDeclarationSyntax.Identifier.ValueText);
}
}
Use
Write a test class
using SourceGenerator.Common;
namespace SourceGenerator.Demo;
public partial class UserClass
{
[Property] private string _test = "test";
[Property] private string _test2;
}
And then restart IDE, You can see the effect , And directly calling the attribute does not report an error 

ending
Only the most basic Source Generator The function of , Limited to space, I can't explain it in depth , The above code can be found here :https://github.com/Weilence/SourceGenerator/ see , At present, the latest code also implements the field generation constructor ,appsettings.json Generate AppSettings Constant field class .
If you just want to use , Can directly nuget install SourceGenerator.Library.
Here is my personal opinion
Source Generator In my opinion, the greatest value is to provide the development experience . As for performance , It can be used Fody Such as the library Emit IL Code , More powerful and perfect , And there are no restrictions on partial classes . But such IL The biggest problem with the library is Design-Time Can't get the generated code , This leads to the need to use some strange methods to generate code .
Source Generator There are many things that can be done in the future , such as
ORM Entity mapping
If the database is Code First, So it's actually okay . But if it is Db First, Mainstream ORM Libraries are generated by commands Model Of , But I don't usually remember orders , Because the frequency of use is not high .
If you add a field later , Or I'll regenerate it , I have to find this order again . Or I'll go by hand C# Add this field to the code , I can guarantee that I can write correctly , But what about the rest of the team ?combination Emit IL technology
It actually says Emit Can't be in Design-Time Used in , But if we use Source Generator Create some empty methods , And then use IL To rewrite , It should be able to solve this problemDependency injection
For now, we are Asp.net Core Service created in , So we need AddSingleton And so on , This is actually very painful , Because first of all, it will appear that the code is very long , Second, this operation is boring and easy to miss .
Now the mainstream framework is adopted Assembly Scan to dynamically register , Avoid adding services manually . But if it passes Source Generator Scan these classes , You can add... At compile time DI ContainersMapping objects
Java There is a library calledMapStruct, The principle is to use maven The plug-in generates static java Code , Then assign values by field .C# I don't seem to see this method in it , At present, I have used Automapper and Tinymapper Do it first Bind, And then use .( Insert a digression ,Tinymapper The previous version did not need Bind, Direct use , But then it was , It seems to solve the problem of multithreading )
Bind It's actually painful , I hate writing this kind of boilerplate code , That I don't want to use this kind of Mapper, direct Json Copy.
版权声明
本文为[Dotnet cross platform]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230301334390.html
边栏推荐
- Basic workflow of CPU
- If the deep replication of objects is realized through C #?
- Judge whether there is a leap year in the given year
- What kind of experience is it to prepare for a month to participate in ACM?
- In redis cluster, the master node fails, and the IP changes after the master-slave switch. The client does not need to deal with it
- Opencv combines multiple pictures into video
- How to use C language to realize [guessing numbers game]
- Systemctl start Prometheus + grafana environment
- Thoughts on the 2022 national network security competition of the national secondary vocational group (only one idea for myself) - network security competition questions (9)
- C read / write binary file
猜你喜欢

Due to 3 ²+ four ²= five ², Therefore, we call '3,4,5' as the number of Pythagorean shares, and find the array of all Pythagorean shares within n (including n).

TP5 email (2020-05-27)

ASP.NET和ASP.NETCore多环境配置对比

Response processing of openfeign

再战leetcode (290.单词规律)

Notes sur le développement de la tarte aux framboises (XII): commencer à étudier la suite UNO - 220 de la tarte aux framboises de contrôle industriel advantech (i): Introduction et fonctionnement du s

tf. keras. layers. Timedistributed function

Er and eer models
![Niuke white moon race 6 [solution]](/img/c5/6c59378c3bb12efa60ab3a8cd2c943.png)
Niuke white moon race 6 [solution]

ASP.NET 6 中间件系列 - 条件中间件
随机推荐
Basic SQL (VIII) data update operation practice
Simple example of using redis in PHP
利用正反遍历来解决“字符的最短距离”问题
7-11 重排链表 (25 分)
PDH optical transceiver 4-way E1 + 4-way 100M Ethernet 4-way 2m optical transceiver FC single fiber 20km rack type
Use split to solve the "most common words" problem
Opencv reads webcam video and saves it locally
tf. keras. layers. MaxPooling? D function
Binary tree
Openfeign details show
c#可变参数params的介绍
Systemctl start Prometheus + grafana environment
Use of MySQL command line client and common commands
C# 11 对 ref 和 struct 的改进
Openfeign timeout setting
Passing object type parameters through openfeign
Service avalanche effect
Summary of software test interview questions
Thoughts on the 2022 national network security competition of the national secondary vocational group (only one idea for myself) - network security competition questions (8)
ASP.NET和ASP.NETCore多环境配置对比