Kaynak Oluşturucular

Bu makalede, .NET Compiler Platform ("Roslyn") SDK'sının bir parçası olarak gelen Kaynak Oluşturuculara genel bir bakış sağlanmaktadır. Kaynak Oluşturucular, C# geliştiricilerinin derlenirken kullanıcı kodunu incelemesine olanak tanır. Oluşturucu, kullanıcının derlemesine eklenen yeni C# kaynak dosyalarını anında oluşturabilir. Bu şekilde, derleme sırasında çalışan bir kodunuz vardır. Kodunuzun geri kalanıyla birlikte derlenmiş ek kaynak dosyaları oluşturmak için programınızı inceler.

Kaynak Oluşturucu, C# geliştiricilerinin yazabileceği ve iki önemli şey yapmanıza olanak tanıyan yeni bir bileşen türüdür:

  1. Derlenen tüm kullanıcı kodunu temsil eden bir derleme nesnesi alın. Bu nesne incelenebilir ve çözümleyicilerde olduğu gibi derlenmekte olan kodun söz dizimi ve anlamsal modelleriyle çalışan bir kod yazabilirsiniz.

  2. Derleme sırasında bir derleme nesnesine eklenebilen C# kaynak dosyaları oluşturun. Başka bir deyişle, kod derlenirken derlemeye giriş olarak ek kaynak kodu sağlayabilirsiniz.

Bu iki şey birleştirildiğinde Kaynak Oluşturucular'ı bu kadar kullanışlı hale getirir. Derleme sırasında derleyicinin derlediğiniz tüm zengin meta verilerle kullanıcı kodunu inceleyebilirsiniz. Ardından oluşturucunuz C# kodunu analiz ettiğiniz verileri temel alan derlemeye geri gönderir. Roslyn Çözümleyicileri hakkında bilginiz varsa Kaynak Oluşturucuları C# kaynak kodunu yayabilen çözümleyiciler olarak düşünebilirsiniz.

Kaynak oluşturucular aşağıda görselleştirilmiş bir derleme aşaması olarak çalışır:

Kaynak oluşturmanın farklı bölümlerini açıklayan grafik

Kaynak Oluşturucu, derleyici tarafından tüm çözümleyicilerle birlikte yüklenen bir .NET Standard 2.0 derlemesidir. .NET Standard bileşenlerinin yüklenip çalıştırılabildiği ortamlarda kullanılabilir.

Önemli

Şu anda kaynak oluşturucu olarak yalnızca .NET Standard 2.0 derlemeleri kullanılabilir.

Genel senaryolar

Bugün teknolojiler tarafından kullanılan analize dayalı olarak kullanıcı kodunu incelemeye ve bilgi veya kod oluşturmaya yönelik üç genel yaklaşım vardır:

  • Çalışma zamanı yansıması.
  • MSBuild görevlerine hokkabazlık yapma.
  • Ara Dil (IL) dokuma (bu makalede ele alınmaz).

Kaynak Oluşturucular her yaklaşım üzerinde bir geliştirme olabilir.

Çalışma zamanı yansıması

Çalışma zamanı yansıması, .NET'e uzun zaman önce eklenen güçlü bir teknolojidir. Bunu kullanmak için sayısız senaryo vardır. Yaygın bir senaryo, bir uygulama başlatıldığında kullanıcı kodunda bazı analizler yapmak ve bu verileri kullanarak bir şeyler oluşturmaktır.

Örneğin, ASP.NET Core web hizmetiniz tanımladığınız yapıları bulmak için ilk kez çalıştığında yansımayı kullanır; böylece denetleyiciler ve razor sayfaları gibi öğeleri "kablolayabilir". Bu, güçlü soyutlamalarla basit kod yazmanıza olanak tanısa da, çalışma zamanında bir performans cezasıyla birlikte gelir: web hizmetiniz veya uygulamanız ilk kez başlatıldığında, kodunuzla ilgili bilgileri bulan tüm çalışma zamanı yansıma kodu çalıştırılana kadar istekleri kabul etmez. Bu performans cezası çok büyük olmasa da, kendi uygulamanızda kendinizi geliştirememenizin sabit bir maliyeti vardır.

Kaynak Oluşturucu ile, başlatmanın denetleyici bulma aşaması bunun yerine derleme zamanında gerçekleşebilir. Oluşturucu, kaynak kodunuzu analiz edebilir ve uygulamanızı "bağlaması" için gereken kodu yayar. Bugün çalışma zamanında gerçekleşen bir eylem derleme zamanına gönderebileceğinden kaynak oluşturucuların kullanılması daha hızlı başlatma sürelerine neden olabilir.

MSBuild görevlerini hokkabazlık yapma

Kaynak Oluşturucular, türleri keşfetmek için çalışma zamanında yansımayla sınırlı olmayan yollarla performansı geliştirebilir. Bazı senaryolar, bir derlemedeki verileri inceleyebilmeleri için MSBuild C# görevini (CSC olarak adlandırılır) birden çok kez çağırmayı içerir. Tahmin edebileceğiniz gibi, derleyiciyi birden çok kez çağırmak, uygulamanızı derlemek için gereken toplam süreyi etkiler. Kaynak oluşturucular yalnızca bazı performans avantajları sunmadığından ve aynı zamanda araçların doğru soyutlama düzeyinde çalışmasına izin verdiğinden, Kaynak Oluşturucular'ın bunun gibi MSBuild görevlerinin hokkabazlık ihtiyacını ortadan kaldırmak için nasıl kullanılabileceğini araştırıyoruz.

Kaynak Oluşturucuların sunabileceği bir diğer özellik de, denetleyiciler ile razor sayfaları arasındaki ASP.NET Core yönlendirmenin nasıl çalıştığı gibi bazı "dize türünde" API'lerin kullanımını gözlemlemektir. Kaynak Oluşturucu ile yönlendirme, derleme zamanı ayrıntısı olarak oluşturulan gerekli dizelerle kesin bir şekilde yazılabilir. Bu, yanlış yazılmış dize değişmez değerinin doğru denetleyiciye isabet etmeyen bir isteğe yol açma sayısını azaltır.

Kaynak oluşturucuları kullanmaya başlama

Bu kılavuzda, API'yi kullanarak bir kaynak oluşturucu oluşturma işlemini ISourceGenerator keşfedeceksiniz.

  1. Bir .NET konsol uygulaması oluşturun. Bu örnekte .NET 6 kullanılmıştır.

  2. sınıfını Program aşağıdaki kodla değiştirin. Aşağıdaki kod en üst düzey deyimlerini kullanmaz. Bu ilk kaynak oluşturucu bu sınıfta kısmi bir yöntem Program yazdığından klasik form gereklidir:

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

    Not

    Bu örneği olduğu gibi çalıştırabilirsiniz, ancak henüz hiçbir şey olmayacaktır.

  3. Ardından, yönteme karşılık gelen yöntemi uygulayacak partial void HelloFrom bir kaynak oluşturucu projesi oluşturacağız.

  4. Hedef çerçeve bilinen adını (TFM) hedefleyen netstandard2.0 bir .NET standart kitaplık projesi oluşturun. Microsoft.CodeAnalysis.Analyzers ve Microsoft.CodeAnalysis.CSharp NuGet paketlerini ekleyin:

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

    İpucu

    Kaynak oluşturucu projesinin TFM'yi hedeflemesi netstandard2.0 gerekir, aksi takdirde çalışmaz.

  5. Kendi Kaynak Oluşturucunuzu belirten HelloSourceGenerator.cs adlı yeni bir C# dosyası oluşturun:

    using Microsoft.CodeAnalysis;
    
    namespace SourceGenerator
    {
        [Generator]
        public class HelloSourceGenerator : ISourceGenerator
        {
            public void Execute(GeneratorExecutionContext context)
            {
                // Code generation goes here
            }
    
            public void Initialize(GeneratorInitializationContext context)
            {
                // No initialization required for this one
            }
        }
    }
    

    Bir kaynak oluşturucunun hem arabirimini Microsoft.CodeAnalysis.ISourceGenerator uygulaması hem de sahip olması Microsoft.CodeAnalysis.GeneratorAttributegerekir. Tüm kaynak oluşturucular başlatma gerektirmez ve bu örnek uygulamada böyle bir durum söz konusudur; burada ISourceGenerator.Initialize boş olur.

  6. yönteminin ISourceGenerator.Execute içeriğini aşağıdaki uygulamayla değiştirin:

    using Microsoft.CodeAnalysis;
    
    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
            }
        }
    }
    

    nesnesinden context derlemelerin giriş noktasına veya Main yöntemine erişebiliriz. mainMethod Örneği bir IMethodSymbolşeklindedir ve bir yöntemi veya yöntem benzeri simgeyi (oluşturucu, yıkıcı, işleç veya özellik/olay erişimcisi dahil) temsil eder. yöntemi, Microsoft.CodeAnalysis.Compilation.GetEntryPoint programın giriş noktası için değerini IMethodSymbol döndürür. Diğer yöntemler, projedeki herhangi bir yöntem simgesini bulmanıza olanak tanır. Bu nesneden, içeren ad alanı (varsa) ve türü hakkında gerekçe verebiliriz. source Bu örnekte, ilişkilendirilmiş deliklerin ad alanı ve tür bilgileriyle doldurulduğu, oluşturulacak kaynak kodu şablonlayan ilişkilendirilmiş bir dizedir. source bir ipucu adıyla öğesine context eklenir. Bu örnekte oluşturucu, konsol uygulamasında yönteminin partial uygulamasını içeren yeni bir oluşturulan kaynak dosyası oluşturur. İstediğiniz kaynağı eklemek için kaynak oluşturucular yazabilirsiniz.

    İpucu

    hintName yöntemindeki GeneratorExecutionContext.AddSource parametre herhangi bir benzersiz ad olabilir. Ad için veya ".generated.cs" gibi ".g.cs" açık bir C# dosya uzantısı sağlamak yaygın bir durum olabilir. Dosya adı, dosyanın kaynak olarak oluşturulduğunu belirlemeye yardımcı olur.

  7. Artık çalışan bir oluşturucumuz var, ancak bunu konsol uygulamamıza bağlamamız gerekiyor. Özgün konsol uygulaması projesini düzenleyin ve proje yolunu yukarıda oluşturduğunuz .NET Standard projesiyle değiştirerek aşağıdakileri ekleyin:

    <!-- Add this as a new ItemGroup, replacing paths and names appropriately -->
    <ItemGroup>
        <ProjectReference Include="..\PathTo\SourceGenerator.csproj"
                          OutputItemType="Analyzer"
                          ReferenceOutputAssembly="false" />
    </ItemGroup>
    

    Bu yeni başvuru geleneksel bir proje başvurusu değildir ve ve ReferenceOutputAssembly özniteliklerini eklemek OutputItemType için el ile düzenlenmesi gerekir. ve öznitelikleri ProjectReferencehakkında OutputItemType daha fazla bilgi için bkz. Ortak MSBuild proje öğeleri: ProjectReferenceReferenceOutputAssembly.

  8. Şimdi konsol uygulamasını çalıştırdığınızda, oluşturulan kodun çalıştırıldığını ve ekrana yazdırıldığını görmeniz gerekir. Konsol uygulamasının HelloFrom kendisi yöntemini uygulamaz, bunun yerine Kaynak Oluşturucu projesinden derleme sırasında oluşturulan kaynaktır. Aşağıdaki metin, uygulamadan alınan örnek bir çıktıdır:

    Generator says: Hi from 'Generated Code'
    

    Not

    Araç deneyimi etkin bir şekilde iyileştirildiğinden IntelliSense'i görmek ve hatalardan kurtulmak için Visual Studio'yu yeniden başlatmanız gerekebilir.

  9. Visual Studio kullanıyorsanız, kaynak tarafından oluşturulan dosyaları görebilirsiniz. Çözüm Gezgini penceresinde Dependencies>Analyzers>SourceGeneratorSourceGenerator.HelloSourceGenerator'ı> genişletin ve Program.g.cs dosyasına çift tıklayın.

    Visual Studio: Kaynak tarafından oluşturulan dosyaları Çözüm Gezgini.

    Bu oluşturulan dosyayı açtığınızda, Visual Studio dosyanın otomatik olarak oluşturulduğunu ve düzenlenebileceğini belirtir.

    Visual Studio: Otomatik olarak oluşturulan Program.g.cs dosyası.

  10. Oluşturulan dosyayı kaydetmek ve oluşturulan dosyaların nerede depolandığını denetlemek için derleme özelliklerini de ayarlayabilirsiniz. Konsol uygulamasının proje dosyasında öğesini öğesine <PropertyGroup>ekleyin <EmitCompilerGeneratedFiles> ve değerini olarak trueayarlayın. Projenizi yeniden oluşturun. Artık oluşturulan dosyalar obj/Debug/net6.0/generated/SourceGenerator/SourceGenerator.HelloSourceGenerator altında oluşturulur. Yol haritasının bileşenleri derleme yapılandırması, hedef çerçeve, kaynak oluşturucu proje adı ve oluşturucunun tam tür adı ile eşlenir. öğesini uygulamanın proje dosyasına ekleyerek <CompilerGeneratedFilesOutputPath> daha kullanışlı bir çıkış klasörü seçebilirsiniz.

Sonraki adımlar

Kaynak Oluşturucular Yemek Kitabı, bu örneklerin bazılarını çözmek için önerilen yaklaşımlarla birlikte ele alır. Ayrıca GitHub'da kendi başınıza deneyebileceğiniz bir dizi örneğimiz vardır.

Kaynak Oluşturucular hakkında daha fazla bilgiyi şu makalelerden edinebilirsiniz: