当前位置:网站首页>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
边栏推荐
- 一套关于 内存对齐 的C#面试题,做错的人很多!
- Traversal of l2-006 tree (middle and later order determination binary tree & sequence traversal)
- Thoughts on the 2022 national network security competition of the national secondary vocational group (only one idea for myself) - network security competition questions (10)
- 最通俗易懂的依赖注入之生命周期
- MYSQL04_ Exercises corresponding to arithmetic, logic, bit, operator and operator
- MYSQL05_ Ordr by sorting, limit grouping, group by grouping
- Vs code setting line feed
- MYSQL_ From mastery to abandonment
- How to write the expected salary on your resume to double your salary during the interview?
- TP5 customization in extend directory succeeded and failed. Return information
猜你喜欢
MYSQL03_ SQL overview, rules and specifications, basic select statements, display table structure
MYSQL05_ Ordr by sorting, limit grouping, group by grouping
Configuring Apache Web services for servers such as Tianyi cloud
利用栈的回溯来解决“文件的最长绝对路径”问题
Response processing of openfeign
ASP.NET 6 中间件系列 - 条件中间件
Introduction to ACM [TSP problem]
Introduction to ACM [inclusion exclusion theorem]
[format] simple output (2)
BLDC double closed loop (speed PI + current PI) Simulink simulation model
随机推荐
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).
基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客?
.NET点滴:说说Middleware构造中获取不到Scoped服务的问题
Wepy learning record
Realize QQ login with PHP
Depth deterministic strategy gradient (ddpg)
Use of MySQL command line client and common commands
Thoughts on the 2022 national network security competition of the national secondary vocational group (only one idea for myself) - network security competition questions (7)
ASP.NET 6 中间件系列 - 条件中间件
使用两种方法来解决“最大回文数乘积”问题
最通俗易懂的依赖注入之生命周期
Service avalanche effect
Close the computer port
交换二叉树中每个结点的左和右
Dynamic sequence table + OJ
Laravel8- use JWT
Restart redis
Binary tree
Thoughts on the 2022 national network security competition of the national secondary vocational group (only one idea for myself) - network security competition questions (10)
Development notes of raspberry pie (12): start Advantech industrial control raspberry pie uno-220 Kit (I): introduction and operation of the system