<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      大家好,我是token。今天想和大家聊聊C#源生成器這個(gè)神奇的技術(shù)。
      說(shuō)起源生成器,可能很多同學(xué)會(huì)想:又是什么新的輪子?我反射用得好好的,為什么要學(xué)這個(gè)?別急,看完這篇文章,你就會(huì)發(fā)現(xiàn)源生成器簡(jiǎn)直是性能優(yōu)化的救命稻草,能讓你的應(yīng)用快到飛起。

      源生成器到底是個(gè)啥?

      簡(jiǎn)單來(lái)說(shuō),源生成器就是一個(gè)在編譯時(shí)幫你寫(xiě)代碼的小助手。想象一下,你有一個(gè)非常勤快的實(shí)習(xí)生,每次編譯的時(shí)候,他都會(huì)根據(jù)你的要求自動(dòng)生成一堆代碼,而且生成的代碼質(zhì)量還特別高。

      傳統(tǒng)的做法是什么樣的呢?比如你想做個(gè)序列化:

      // 老式做法:運(yùn)行時(shí)反射,慢得像蝸牛
      var json = JsonSerializer.Serialize(person); // 內(nèi)部大量反射調(diào)用
      

      而源生成器的做法是:

      // 編譯時(shí)就生成好了序列化代碼,快得像火箭
      var json = JsonSerializer.Serialize(person, PersonContext.Default.Person);
      

      看起來(lái)差不多,但實(shí)際性能差了一個(gè)天地。

      為什么源生成器這么快?

      數(shù)據(jù)說(shuō)話最有說(shuō)服力。在序列化場(chǎng)景中,傳統(tǒng)反射需要734.563納秒,而源生成器只需要6.253納秒。這是117倍的性能提升!

      為什么會(huì)有這么大的差距呢?

      反射的痛點(diǎn):

      • 運(yùn)行時(shí)才開(kāi)始分析類(lèi)型結(jié)構(gòu)
      • 需要緩存和管理大量元數(shù)據(jù)
      • 每次調(diào)用都有裝箱拆箱的開(kāi)銷(xiāo)
      • GC壓力山大

      源生成器的優(yōu)勢(shì):

      • 編譯時(shí)就把所有工作做完了
      • 生成的代碼直接調(diào)用,沒(méi)有中間層
      • 零反射,零裝箱
      • 內(nèi)存占用更低

      就像是你要做一道菜,反射是現(xiàn)場(chǎng)買(mǎi)菜現(xiàn)場(chǎng)切,而源生成器是提前把所有食材都準(zhǔn)備好,直接下鍋。

      第一個(gè)源生成器:Hello World

      讓我們來(lái)寫(xiě)一個(gè)最簡(jiǎn)單的源生成器。首先創(chuàng)建一個(gè)新的類(lèi)庫(kù)項(xiàng)目:

      <Project Sdk="Microsoft.NET.Sdk">
        <PropertyGroup>
          <TargetFramework>netstandard2.0</TargetFramework>
          <IsRoslynComponent>true</IsRoslynComponent>
          <IncludeBuildOutput>false</IncludeBuildOutput>
        </PropertyGroup>
        
        <ItemGroup>
          <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
          <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
        </ItemGroup>
      </Project>
      

      然后寫(xiě)一個(gè)簡(jiǎn)單的生成器:

      [Generator]
      public class HelloWorldGenerator : ISourceGenerator
      {
          public void Initialize(GeneratorInitializationContext context)
          {
              // 初始化,一般用不到
          }
      
          public void Execute(GeneratorExecutionContext context)
          {
              var sourceCode = @"
      namespace Generated
      {
          public static class HelloWorld
          {
              public static string SayHello() => ""Hello from Source Generator!"";
          }
      }";
              context.AddSource("HelloWorld.g.cs", sourceCode);
          }
      }
      

      在消費(fèi)項(xiàng)目中引用這個(gè)生成器:

      <ProjectReference Include="../HelloWorldGenerator/HelloWorldGenerator.csproj" 
                        OutputItemType="Analyzer" 
                        ReferenceOutputAssembly="false" />
      

      現(xiàn)在你可以直接使用生成的代碼:

      using Generated;
      
      Console.WriteLine(HelloWorld.SayHello()); // 輸出: Hello from Source Generator!
      

      進(jìn)階技巧:增量源生成器

      上面的例子雖然能工作,但在大型項(xiàng)目中會(huì)有性能問(wèn)題。每次編譯都會(huì)重新生成所有代碼,就像每次做飯都要重新洗所有的鍋一樣浪費(fèi)。

      這時(shí)候就要用到增量源生成器了。它采用了類(lèi)似React的虛擬DOM的思想,只有變化的部分才會(huì)重新生成:

      [Generator]
      public class SmartPropertyGenerator : IIncrementalGenerator
      {
          public void Initialize(IncrementalGeneratorInitializationContext context)
          {
              // 只關(guān)注有特定屬性的類(lèi)
              var pipeline = context.SyntaxProvider
                  .ForAttributeWithMetadataName(
                      "MyNamespace.GeneratePropertiesAttribute",
                      predicate: static (node, _) => node is ClassDeclarationSyntax,
                      transform: static (ctx, _) => GetClassInfo(ctx))
                  .Where(static m => m is not null);
              
              context.RegisterSourceOutput(pipeline, GenerateProperties);
          }
          
          private static ClassInfo? GetClassInfo(GeneratorAttributeSyntaxContext context)
          {
              var classDeclaration = (ClassDeclarationSyntax)context.TargetNode;
              var symbol = context.TargetSymbol as INamedTypeSymbol;
              
              return new ClassInfo(
                  Name: symbol.Name,
                  Namespace: symbol.ContainingNamespace.ToDisplayString()
              );
          }
          
          private static void GenerateProperties(SourceProductionContext context, ClassInfo classInfo)
          {
              var source = $@"
      namespace {classInfo.Namespace}
      {{
          partial class {classInfo.Name}
          {{
              public string GeneratedProperty {{ get; set; }} = ""Auto-generated!"";
          }}
      }}";
              context.AddSource($"{classInfo.Name}.Properties.g.cs", source);
          }
      }
      
      public record ClassInfo(string Name, string Namespace);
      

      使用的時(shí)候只需要加個(gè)屬性:

      [GenerateProperties]
      public partial class Person
      {
          public string Name { get; set; }
          // GeneratedProperty 會(huì)被自動(dòng)生成
      }
      

      實(shí)戰(zhàn)案例:FastService的妙用

      說(shuō)到實(shí)際應(yīng)用,不得不提一下FastService這個(gè)項(xiàng)目。它用源生成器簡(jiǎn)化了ASP.NET Core的API開(kāi)發(fā),讓你寫(xiě)API就像寫(xiě)普通方法一樣簡(jiǎn)單。

      傳統(tǒng)的Minimal API寫(xiě)法:

      app.MapGet("/api/users", async (UserService service) => 
      {
          return await service.GetUsersAsync();
      });
      
      app.MapPost("/api/users", async (CreateUserRequest request, UserService service) =>
      {
          return await service.CreateUserAsync(request);
      });
      
      // 還有一大堆路由配置...
      

      用了FastService之后:

      [Route("/api/users")]
      [Tags("用戶(hù)管理")]
      public class UserService : FastApi
      {
          [EndpointSummary("獲取用戶(hù)列表")]
          public async Task<List<User>> GetUsersAsync()
          {
              return await GetAllUsersAsync();
          }
          
          [EndpointSummary("創(chuàng)建用戶(hù)")]
          public async Task<User> CreateUserAsync(CreateUserRequest request)
          {
              return await SaveUserAsync(request);
          }
      }
      

      源生成器會(huì)自動(dòng)分析方法名,推斷HTTP方法類(lèi)型:

      • Get* → GET請(qǐng)求
      • Create*, Add*, Post* → POST請(qǐng)求
      • Update*, Edit*, Put* → PUT請(qǐng)求
      • Delete*, Remove* → DELETE請(qǐng)求

      然后生成對(duì)應(yīng)的路由注冊(cè)代碼。這樣既保持了強(qiáng)類(lèi)型的優(yōu)勢(shì),又大大簡(jiǎn)化了代碼編寫(xiě)。

      性能優(yōu)化的秘密武器

      在開(kāi)發(fā)源生成器時(shí),有幾個(gè)性能優(yōu)化的小技巧:

      1. 早期過(guò)濾

      不要什么節(jié)點(diǎn)都分析,先用謂詞函數(shù)過(guò)濾掉不需要的:

      var pipeline = context.SyntaxProvider
          .CreateSyntaxProvider(
              predicate: static (node, _) => node is ClassDeclarationSyntax cls && 
                                            cls.AttributeLists.Count > 0, // 只看有屬性的類(lèi)
              transform: static (ctx, _) => TransformNode(ctx))
      

      2. 使用值類(lèi)型數(shù)據(jù)模型

      千萬(wàn)不要在數(shù)據(jù)模型中保存Syntax或ISymbol對(duì)象,它們不能被正確緩存:

      // ? 錯(cuò)誤做法
      public record ClassInfo(ClassDeclarationSyntax Syntax, INamedTypeSymbol Symbol);
      
      // ? 正確做法
      public readonly record struct ClassInfo(
          string Name,
          string Namespace,
          EquatableArray<PropertyInfo> Properties);
      

      3. 對(duì)象池優(yōu)化

      對(duì)于頻繁創(chuàng)建的對(duì)象,使用對(duì)象池:

      private static readonly ObjectPool<StringBuilder> _stringBuilderPool = 
          new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy());
      
      private static string GenerateCode(ClassInfo info)
      {
          var sb = _stringBuilderPool.Get();
          try
          {
              sb.AppendLine($"namespace {info.Namespace}");
              // 生成代碼...
              return sb.ToString();
          }
          finally
          {
              _stringBuilderPool.Return(sb);
          }
      }
      

      調(diào)試技巧:不再抓瞎

      源生成器的調(diào)試曾經(jīng)是個(gè)大難題,但現(xiàn)在有了不少好用的技巧。

      1. 斷點(diǎn)調(diào)試

      在源生成器代碼中加入:

      public void Initialize(IncrementalGeneratorInitializationContext context)
      {
      #if DEBUG
          if (!Debugger.IsAttached)
          {
              Debugger.Launch(); // 會(huì)彈出調(diào)試器選擇界面
          }
      #endif
      }
      

      2. 查看生成的代碼

      在項(xiàng)目文件中加入:

      <PropertyGroup>
        <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
        <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
      </PropertyGroup>
      

      編譯后,生成的代碼會(huì)保存在Generated文件夾中,你可以直接查看。

      3. 單元測(cè)試

      寫(xiě)個(gè)測(cè)試來(lái)驗(yàn)證生成器的行為:

      [Test]
      public void Should_Generate_Property_For_Marked_Class()
      {
          var source = @"
      using MyNamespace;
      
      [GenerateProperties]
      public partial class TestClass
      {
      }";
      
          var result = RunGenerator(source);
          
          Assert.That(result, Contains.Substring("public string GeneratedProperty"));
      }
      

      常見(jiàn)陷阱與避坑指南

      開(kāi)發(fā)源生成器時(shí),有幾個(gè)坑是新手經(jīng)常掉進(jìn)去的:

      1. 命名空間沖突

      生成的代碼可能和現(xiàn)有代碼沖突,記得加上合適的命名空間或者前綴:

      // 生成的代碼加上特殊前綴
      var className = $"Generated_{originalClassName}";
      

      2. 編譯錯(cuò)誤處理

      當(dāng)生成的代碼有語(yǔ)法錯(cuò)誤時(shí),編譯器的錯(cuò)誤信息可能不夠清晰。建議在生成器中添加診斷信息:

      public static readonly DiagnosticDescriptor InvalidClassError = new(
          id: "SG001",
          title: "Invalid class for generation",
          messageFormat: "The class '{0}' must be partial to use this generator",
          category: "SourceGenerator",
          DiagnosticSeverity.Error,
          isEnabledByDefault: true);
      
      // 在生成器中使用
      context.ReportDiagnostic(Diagnostic.Create(InvalidClassError, location, className));
      

      3. 增量生成緩存失效

      如果數(shù)據(jù)模型設(shè)計(jì)不當(dāng),可能導(dǎo)致緩存頻繁失效:

      // ? 這樣會(huì)導(dǎo)致緩存失效,因?yàn)镃ompilation對(duì)象每次都不同
      var hasRef = context.Compilation.Select(comp => comp.ReferencedAssemblyNames.Any(...));
      
      // ? 正確的做法
      var hasRef = context.CompilationProvider.Select(comp => 
          comp.ReferencedAssemblyNames.Select(name => name.Name).OrderBy(x => x).ToArray());
      

      生態(tài)系統(tǒng)現(xiàn)狀

      目前已經(jīng)有不少成熟的源生成器項(xiàng)目:

      • System.Text.Json - 微軟官方的JSON序列化優(yōu)化
      • Mapperly - 對(duì)象映射生成器
      • FastService - API開(kāi)發(fā)簡(jiǎn)化
      • StronglyTypedId - 強(qiáng)類(lèi)型ID生成
      • Meziantou.Framework.StronglyTypedId - 另一個(gè)強(qiáng)類(lèi)型ID實(shí)現(xiàn)

      這些項(xiàng)目都是學(xué)習(xí)源生成器的好例子,推薦大家去看看源碼。

      總結(jié)

      源生成器真的是一個(gè)很酷的技術(shù)。它不僅能大幅提升應(yīng)用性能,還能讓我們寫(xiě)出更簡(jiǎn)潔、更高效的代碼。雖然學(xué)習(xí)曲線有點(diǎn)陡峭,但一旦掌握了,你會(huì)發(fā)現(xiàn)很多以前覺(jué)得復(fù)雜的問(wèn)題都能用源生成器優(yōu)雅地解決。

      如果你還在用傳統(tǒng)的反射做序列化、映射這些工作,不妨試試源生成器。相信我,一旦體驗(yàn)過(guò)那種編譯時(shí)生成代碼的快感,你就再也回不去了。

      最后,學(xué)習(xí)新技術(shù)最好的方法就是動(dòng)手實(shí)踐。建議大家從簡(jiǎn)單的Hello World開(kāi)始,然后逐步嘗試更復(fù)雜的場(chǎng)景。記住,代碼是寫(xiě)給人看的,源生成器也不例外。寫(xiě)出清晰、可維護(hù)的生成器代碼,比寫(xiě)出復(fù)雜炫技的代碼更有價(jià)值。

      Happy coding!

      posted on 2025-07-12 00:31  239573049  閱讀(2075)  評(píng)論(2)    收藏  舉報(bào)



      主站蜘蛛池模板: 热久久这里只有精品国产| 性人久久久久| 亚洲国产欧美一区二区好看电影| 白丝乳交内射一二三区| 日韩一区二区三区女优丝袜| 国产中文三级全黄| 色综合 图片区 小说区| 国产精选一区二区三区| 久久综合给合久久狠狠狠88| 国产国产久热这里只有精品| 狠狠色狠狠色五月激情| 日日爽日日操| 亚洲精品日韩中文字幕| 肉色丝袜足j视频国产| 国产精品亚洲综合一区二区 | 欧美成人精品在线| 波多野结衣av高清一区二区三区| 国产精品日韩深夜福利久久| 亚洲av无码成人精品区一区| 男女性杂交内射女bbwxz| 国产成年码av片在线观看| 尤物视频色版在线观看| 四虎国产成人永久精品免费| 久久99精品久久久久久| 日韩精品一二区在线观看| 欧美另类精品xxxx人妖| 欧美交a欧美精品喷水| 91久久国产成人免费观看| 蜜桃网址| 2021亚洲va在线va天堂va国产| 真人抽搐一进一出视频| 国产精品美女免费无遮挡| 永新县| 亚洲精品综合网二三区| 国产精品毛片av999999| 久久99精品久久久久久齐齐| 亚洲天堂激情av在线| 精品人妻人人做人人爽| 美腿丝袜亚洲综合在线视频| 日本肉体xxxx裸交| 亚洲av一本二本三本|