亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解C#中有趣的?SourceGenerator生成器

 更新時(shí)間:2024年10月08日 09:38:57   作者:一線碼農(nóng)  
源生成器是Roslyn編譯器的一個(gè)強(qiáng)大特性,允許開發(fā)者在編譯階段注入自定義代碼,本文通過一個(gè)實(shí)例探究源生成器的使用方法及其在AOT場(chǎng)景的應(yīng)用,并利用WinDbg工具深入分析Roslyn的內(nèi)部機(jī)制

一:背景

1. 講故事

前些天在看 AOT的時(shí)候關(guān)注了下 源生成器,挺有意思的一個(gè)東西,今天寫一篇文章簡(jiǎn)單的分享下。

二:源生成器探究之旅

1. 源生成器是什么

簡(jiǎn)單來說,源生成器是Roslyn編譯器給程序員開的一道口子,在這個(gè)口子里可以塞入一些自定義的cs代碼,讓Roslyn編譯器在編譯代碼的時(shí)候順帶給一起處理了,簡(jiǎn)單的說就是 夾帶私貨 ,但古話又說 師不順路, 醫(yī)不叩門,所以還是比較尷尬的,看一下官方給的圖,圖中的橙色區(qū)域就是夾帶的私貨。

有些朋友肯定好奇,這玩意有什么用?其實(shí)在AOT領(lǐng)域中,JsonSerializer 就使用了 SourceGeneration 來給序列化的類型(WeatherForecast)生成元數(shù)據(jù),參考代碼如下:

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

2. 一個(gè)簡(jiǎn)單的例子

上面的例子不過多深入,先看看怎么實(shí)現(xiàn)0到1的問題,這里使用官方例子,用鉤子來實(shí)現(xiàn) 分布方法 的方法體。

新建 SourceGenerator 類庫項(xiàng)目

這里面的source就是鉤子代碼,不過目前只能是.NET Standard 2.0項(xiàng)目,應(yīng)該是要達(dá)到最大的兼容性,參考代碼如下:

namespace SourceGenerator
{
    [Generator]
    public class HelloSourceGenerator : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            // Find the main method
            var mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken);
            // Build up the source code
            string source = $@"
                                // <auto-generated/>
                                   using System;
                                   namespace {mainMethod.ContainingNamespace.ToDisplayString()}
                                   {{
                                       public static partial class {mainMethod.ContainingType.Name}
                                       {{
                                           static partial void HelloFrom(string name) =>
                                               Console.WriteLine($""Generator says: Hi from '{{name}}'"");
                                       }}
                                   }}
                              ";
            var typeName = mainMethod.ContainingType.Name;
            // Add the source code to the compilation
            context.AddSource($"{typeName}.g.cs", source);
        }
        public void Initialize(GeneratorInitializationContext context)
        {
            // No initialization required for this one
        }
    }
}

新建 Example_21_15 項(xiàng)目

這是一個(gè)控制臺(tái)程序,引用剛才的項(xiàng)目,并聲明部分方法 HelloFrom,參考代碼如下:

namespace Example_21_15
{
    partial class Program
    {
        static void Main(string[] args)
        {
            HelloFrom("Generated Code");
            Console.ReadLine();
        }
        static partial void HelloFrom(string name);
    }
}

要記住在 Example_21_15.csproj 中 Include 時(shí)要額外增加兩個(gè)參數(shù),參考如下:

<ItemGroup>
		<ProjectReference Include="..\SourceGenerator\SourceGenerator.csproj"
						  OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
	</ItemGroup>

配置好之后就可以把程序跑起來了,可以看到方法體確實(shí)是鉤子中的代碼。

三:Roslyn如何夾帶私貨

1. windbg調(diào)試

究竟是如何夾帶私貨,本質(zhì)上是 Roslyn 內(nèi)部的邏輯,現(xiàn)在的問題是如何給他挖出來了呢?這就需要使用強(qiáng)大的 windbg,采用exe啟動(dòng)劫持的方式洞察,流程步驟如下:

windbg 的exe劫持

資深的 WinDbg 玩家我相信都知道這個(gè)招數(shù),我寫了一個(gè)簡(jiǎn)單的 bat 腳本,對(duì) dotnet.exe 進(jìn)行啟動(dòng)攔截,要提醒的是有安全軟件的話可以先卸載掉,以免出現(xiàn)無權(quán)限的問題。

SET ApplicationName=dotnet.exe
SET WinDbgPath=C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\%ApplicationName%" /v debugger  /t REG_SZ  /d "%WinDbgPath%" /f
ECHO 已成功設(shè)置
PAUSE 

修改狗子代碼

最簡(jiǎn)單粗暴的方式就是加 Debugger.Break,好讓他在 windbg 中自動(dòng)中斷。

public class HelloSourceGenerator : ISourceGenerator
{
    public void Execute(GeneratorExecutionContext context)
    {
        Debugger.Break();
        //...
    }
}

使用 dotnet publish 發(fā)布程序

所有的埋伏做好之后,最后就是用 dotnet publish 來引誘 Roslyn 出洞,參考命令如下 dotnet publish -r win-x64 -c Debug -o D:\testdump, 命令執(zhí)行之后果然給攔截到了,截圖如下:

由于調(diào)用棧難得,再弄一份文字版。

0:029> !clrstack
OS Thread Id: 0x443c (29)
        Child SP               IP Call Site
00000068E603DD18 00007ffe0135d962 [HelperMethodFrame: 00000068e603dd18] System.Diagnostics.Debugger.BreakInternal()
00000068E603DE20 00007ffd6dd357da System.Diagnostics.Debugger.Break() [/_/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs @ 18]
00000068E603DE50 00007ffd13920522 SourceGenerator.HelloSourceGenerator.Execute(Microsoft.CodeAnalysis.GeneratorExecutionContext)
00000068E603DF10 00007ffd6a4ad4ac Microsoft.CodeAnalysis.SourceGeneratorAdaptor.b__5_5(Microsoft.CodeAnalysis.SourceProductionContext, GeneratorContextBuilder) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorAdaptor.cs @ 55]
00000068E603E020 00007ffd6a5bfdd8 Microsoft.CodeAnalysis.UserFunctionExtensions+c__DisplayClass3_0`2[[Microsoft.CodeAnalysis.SourceProductionContext, Microsoft.CodeAnalysis],[System.__Canon, System.Private.CoreLib]].b__0(Microsoft.CodeAnalysis.SourceProductionContext, System.__Canon, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/UserFunction.cs @ 101]
00000068E603E070 00007ffd6a624e9c Microsoft.CodeAnalysis.SourceOutputNode`1[[System.__Canon, System.Private.CoreLib]].UpdateStateTable(Builder, Microsoft.CodeAnalysis.NodeStateTable`1,System.Collections.Generic.IEnumerable`1>>, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs @ 70]
00000068E603E2B0 00007ffd13fd1ed8 Microsoft.CodeAnalysis.DriverStateTable+Builder.GetLatestStateTableForNode[[System.ValueTuple`2[[System.__Canon, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib]], System.Private.CoreLib]](Microsoft.CodeAnalysis.IIncrementalGeneratorNode`1>) [/_/src/Compilers/Core/Portable/SourceGeneration/Nodes/DriverStateTable.cs @ 60]
00000068E603E320 00007ffd6a625349 Microsoft.CodeAnalysis.SourceOutputNode`1[[System.__Canon, System.Private.CoreLib]].AppendOutputs(Microsoft.CodeAnalysis.IncrementalExecutionContext, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/Nodes/SourceOutputNode.cs @ 102]
00000068E603E460 00007ffd6a4b0837 Microsoft.CodeAnalysis.GeneratorDriver.UpdateOutputs(System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.IncrementalGeneratorOutputKind, Builder, System.Threading.CancellationToken, Builder) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @ 340]
00000068E603E520 00007ffd6a4afc9b Microsoft.CodeAnalysis.GeneratorDriver.RunGeneratorsCore(Microsoft.CodeAnalysis.Compilation, Microsoft.CodeAnalysis.DiagnosticBag, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @ 303]
00000068E603ECB0 00007ffd6a4adfc1 Microsoft.CodeAnalysis.GeneratorDriver.RunGeneratorsAndUpdateCompilation(Microsoft.CodeAnalysis.Compilation, Microsoft.CodeAnalysis.Compilation ByRef, System.Collections.Immutable.ImmutableArray`1 ByRef, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @ 54]
00000068E603EE50 00007ffd6a468763 Microsoft.CodeAnalysis.CommonCompiler.RunGenerators(Microsoft.CodeAnalysis.Compilation, System.String, Microsoft.CodeAnalysis.ParseOptions, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.DiagnosticBag) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 838]
00000068E603EF20 00007ffd6a469520 Microsoft.CodeAnalysis.CommonCompiler.CompileAndEmit(Microsoft.CodeAnalysis.TouchedFileLogger, Microsoft.CodeAnalysis.Compilation ByRef, System.Collections.Immutable.ImmutableArray`1, System.Collections.Immutable.ImmutableArray`1, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.AnalyzerConfigSet, System.Collections.Immutable.ImmutableArray`1, System.Collections.Immutable.ImmutableArray`1, Microsoft.CodeAnalysis.DiagnosticBag, Microsoft.CodeAnalysis.ErrorLogger, System.Threading.CancellationToken, System.Threading.CancellationTokenSource ByRef, Microsoft.CodeAnalysis.Diagnostics.AnalyzerDriver ByRef, System.Nullable`1 ByRef) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 1145]
00000068E603F280 00007ffd6a468c52 Microsoft.CodeAnalysis.CommonCompiler.RunCore(System.IO.TextWriter, Microsoft.CodeAnalysis.ErrorLogger, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 956]
00000068E603F450 00007ffd6a4684b6 Microsoft.CodeAnalysis.CommonCompiler.Run(System.IO.TextWriter, System.Threading.CancellationToken) [/_/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @ 781]
00000068E603F4C0 00007ffd6aaf9def Microsoft.CodeAnalysis.CompilerServer.CompilerServerHost.RunCompilation(Microsoft.CodeAnalysis.CompilerServer.RunRequest ByRef, System.Threading.CancellationToken) [/_/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs @ 152]
00000068E603F5E0 00007ffd6aaff745 Microsoft.CodeAnalysis.CompilerServer.ClientConnectionHandler+c__DisplayClass8_0.b__1() [/_/src/Compilers/Server/VBCSCompiler/ClientConnectionHandler.cs @ 168]
00000068E603F640 00007ffd6de7b78f System.Threading.Tasks.Task`1[[System.__Canon, System.Private.CoreLib]].InnerInvoke() [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs @ 501]
00000068E603F680 00007ffd6dc364bd System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 179]
00000068E603F6F0 00007ffd6dc50914 System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2345]
00000068E603F9C0 00007ffd72d1c663 [DebuggerU2MCatchHandlerFrame: 00000068e603f9c0] 

最后一個(gè)問題就是如果找到這個(gè)調(diào)用棧上的源碼,當(dāng)然是在 github 上找啦: https://github.com/dotnet/roslyn ,拉下來后就可以根據(jù)調(diào)用棧上的方法來分析啦,參考如下:

四:總結(jié)

在研究底層方面,windbg可謂是一把趁手的兵器,這個(gè)例子也算是活生生的論證了一把,否則真的不知道從 Roslyn 何處來論證官方給出的流程圖,對(duì)吧。

到此這篇關(guān)于詳解C#中有趣的 SourceGenerator生成器的文章就介紹到這了,更多相關(guān)C# SourceGenerator生成器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#?winform實(shí)現(xiàn)多語言切換功能

    C#?winform實(shí)現(xiàn)多語言切換功能

    這篇文章主要為大家詳細(xì)介紹了如何使用C#?winform實(shí)現(xiàn)多語言切換功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下
    2024-02-02
  • C#處理和對(duì)接HTTP接口請(qǐng)求的方法

    C#處理和對(duì)接HTTP接口請(qǐng)求的方法

    下面通過四步給大家介紹了c#處理和對(duì)接http接口請(qǐng)求的方法,分步驟介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起看下吧
    2016-08-08
  • C#連接Oracle數(shù)據(jù)庫使用Oracle.ManagedDataAccess.dll

    C#連接Oracle數(shù)據(jù)庫使用Oracle.ManagedDataAccess.dll

    這篇文章主要介紹了C#使用Oracle.ManagedDataAccess.dll的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-11-11
  • C#使用Streamwriter打開文件的方法

    C#使用Streamwriter打開文件的方法

    這篇文章主要介紹了C#使用Streamwriter打開文件的方法,涉及C#操作文件的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • C# Winform實(shí)現(xiàn)表格復(fù)制粘貼效果

    C# Winform實(shí)現(xiàn)表格復(fù)制粘貼效果

    這篇文章主要為大家學(xué)習(xí)介紹了如何通過C# Winform實(shí)現(xiàn)表格復(fù)制粘貼效果,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下
    2023-07-07
  • C#實(shí)現(xiàn)異步GET的方法

    C#實(shí)現(xiàn)異步GET的方法

    這篇文章主要介紹了C#實(shí)現(xiàn)異步GET的方法,涉及C#異步請(qǐng)求的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • C#實(shí)現(xiàn)一階卡爾曼濾波算法的示例代碼

    C#實(shí)現(xiàn)一階卡爾曼濾波算法的示例代碼

    這篇文章主要介紹了C#實(shí)現(xiàn)一階卡爾曼濾波算法的示例代碼,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-04-04
  • C#使用系統(tǒng)方法發(fā)送異步郵件完整實(shí)例

    C#使用系統(tǒng)方法發(fā)送異步郵件完整實(shí)例

    這篇文章主要介紹了C#使用系統(tǒng)方法發(fā)送異步郵件實(shí)現(xiàn)方法,結(jié)合完整實(shí)例形式分析了C#異步調(diào)用與郵件發(fā)送的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • c#循環(huán)左移字符示例

    c#循環(huán)左移字符示例

    這篇文章主要介紹了c#循環(huán)左移字符示例,需要的朋友可以參考下
    2014-04-04
  • C#純技術(shù)之Class寫入Json

    C#純技術(shù)之Class寫入Json

    這篇文章主要介紹了C#純技術(shù)之Class寫入Json問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01

最新評(píng)論