Bagikan melalui


Generator Sumber

Artikel ini memberikan ringkasan tentang Generator Sumber yang dikirimkan sebagai bagian dari .NET Compiler Platform ("Roslyn") SDK. Generator Sumber memungkinkan pengembang C# memeriksa kode pengguna saat sedang dikompilasi. Generator dapat membuat file sumber C# baru dengan cepat yang ditambahkan ke kompilasi pengguna. Dengan cara ini, Anda memiliki kode yang terus berjalan selama kompilasi. Hal ini memeriksa program Anda untuk menghasilkan file sumber tambahan yang dikompilasi bersamaan dengan sisa kode Anda.

Generator Sumber adalah jenis komponen baru yang dapat ditulis oleh pengembang C# yang memungkinkan Anda melakukan dua hal utama:

  1. Ambil objek kompilasi yang mewakili semua kode pengguna yang sedang dikompilasi. Objek ini dapat diperiksa, dan Anda dapat menulis kode yang berfungsi dengan sintaks dan model semantik untuk kode yang sedang dikompilasi, seperti halnya dengan analis saat ini.

  2. Hasilkan file sumber C# yang dapat ditambahkan ke objek kompilasi selama proses kompilasi. Dengan kata lain, Anda dapat memberikan kode sumber tambahan sebagai input ke kompilasi saat kode sedang dikompilasi.

Jika digabungkan, kedua hal inilah yang membuat Generator Sumber sangat berguna. Anda dapat memeriksa kode pengguna dengan semua metadata kaya yang dibuat oleh kompilator selama kompilasi. Generator Anda kemudian menunjukkan kode C# kembali ke kompilasi yang sama yang didasarkan pada data yang telah Anda analisis. Jika Anda terbiasa dengan Roslyn Analyzers, Anda dapat menganggap Generator Sumber sebagai analis yang dapat menunjukkan kode sumber C#.

Generator sumber berjalan sebagai fase kompilasi yang divisualisasikan di bawah ini:

Grafik yang menjelaskan berbagai bagian pembuatan sumber

Generator Sumber adalah rakitan .NET Standar 2.0 yang dimuat oleh kompilator bersamaan dengan analis. Ini dapat digunakan di lingkungan tempat komponen .NET Standar dapat dimuat dan dijalankan.

Penting

Saat ini hanya rakitan .NET Standar 2.0 yang dapat digunakan sebagai Generator Sumber.

Skenario umum

Ada tiga pendekatan umum untuk memeriksa kode pengguna dan menghasilkan informasi atau kode berdasarkan analisis yang digunakan oleh teknologi saat ini:

  • Refleksi runtime.
  • Menjalankan tugas MSBuild.
  • Pembentukan Bahasa Perantara (IL) (tidak dibahas dalam artikel ini).

Generator Sumber dapat menjadi sebuah peningkatan atas setiap pendekatan.

Refleksi durasi

Refleksi durasi adalah teknologi canggih yang ditambahkan ke .NET sejak lama. Ada banyak skenario untuk menggunakannya. Skenario umum adalah melakukan beberapa analisis kode pengguna saat aplikasi dimulai dan menggunakan data tersebut untuk menghasilkan sesuatu.

Misalnya, ASP.NET Core menggunakan refleksi saat layanan web Anda pertama kali berjalan untuk menemukan konstruksi yang telah Anda tetapkan sehingga dapat "menyambungkan" hal-hal seperti pengontrol dan halaman razor. Meskipun ini memungkinkan Anda untuk menulis kode langsung dengan abstraksi yang kuat, hal tersebut datang dengan penalti kinerja pada waktu berjalan: ketika layanan web atau aplikasi Anda pertama kali dijalankan, itu tidak dapat menerima permintaan apa pun sampai semua kode refleksi runtime yang menunjukkan informasi bahwa kode Anda selesai dijalankan. Meskipun penalti kinerja ini tidak besar, ini adalah biaya tetap yang tidak dapat Anda tingkatkan sendiri di aplikasi Anda sendiri.

Dengan Generator Sumber, fase penemuan pengontrol startup dapat terjadi pada saat kompilasi. Generator dapat menganalisis kode sumber Anda dan menunjukkan kode yang diperlukan untuk "menyambungkan" aplikasi Anda. Menggunakan generator sumber membuat waktu startup lebih cepat, karena tindakan yang terjadi pada waktu proses hari ini dapat didorong ke waktu kompilasi.

Menjalankan tugas MSBuild

Generator Sumber dapat meningkatkan performa dengan cara yang tidak hanya dibatasi pada refleksi durasi yang juga untuk menemukan jenis. Beberapa skenario melibatkan panggilan tugas MSBuild C# (disebut CSC) beberapa kali sehingga mereka dapat memeriksa data dari kompilasi. Seperti yang mungkin Anda bayangkan, memanggil kompilator lebih dari sekali memengaruhi total waktu yang dibutuhkan untuk membangun aplikasi Anda. Kami sedang menyelidiki bagaimana Generator Sumber dapat digunakan untuk menghilangkan kebutuhan untuk menjalankan tugas MSBuild seperti ini, karena generator Sumber tidak hanya menawarkan beberapa manfaat performa, tetapi juga memungkinkan alat untuk beroperasi pada tingkat abstraksi yang tepat.

Kemampuan lain yang dapat ditawarkan Generator Sumber adalah mewajibkan penggunaan beberapa API "diketik dengan string", seperti cara kerja perutean ASP.NET Core antara pengontrol dan halaman razor. Dengan Generator Sumber, perutean dapat diketik dengan kuat dengan string penting yang dihasilkan sebagai detail waktu kompilasi. Ini akan mengurangi berapa kali string yang salah ketik secara harfiah mengarah ke permintaan yang tidak mencapai pengontrol yang tepat.

Memulai dengan generator sumber

Dalam panduan ini, Anda akan menjelajahi pembuatan generator sumber menggunakan ISourceGenerator API.

  1. Buat aplikasi konsol .NET baru. Contoh ini menggunakan .NET 6.

  2. Ganti kelas Program Anda dengan kode berikut. Kode berikut tidak menggunakan pernyataan tingkat atas. Formulir klasik diperlukan karena generator sumber pertama ini menulis metode parsial di kelas tersebut Program:

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

    Catatan

    Anda dapat menjalankan sampel ini apa adanya, tetapi tidak akan terjadi apa-apa.

  3. Selanjutnya, kita akan membuat proyek generator sumber yang akan mengimplementasikan padanan metode partial void HelloFrom.

  4. Buat proyek pustaka standar .NET yang menargetkan bingkai target moniker (TFM) netstandard2.0. Tambahkan paket NuGet Microsoft.CodeAnalysis.Analyzers dan Microsoft.CodeAnalysis.CSharp:

    <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>
    

    Tip

    Proyek generator sumber perlu menargetkan TFM netstandard2.0, jika tidak, maka tidak akan berfungsi.

  5. Buat file C# baru bernama HelloSourceGenerator.cs yang menentukan Generator Sumber Anda sendiri seperti:

    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
            }
        }
    }
    

    Generator sumber perlu mengimplementasikan antarmuka Microsoft.CodeAnalysis.ISourceGenerator, dan memiliki Microsoft.CodeAnalysis.GeneratorAttribute. Tidak semua generator sumber memerlukan inisialisasi, dan itulah yang terjadi dengan contoh implementasi ini—di mana ISourceGenerator.Initialize kosong.

  6. Ganti konten metode ISourceGenerator.Execute dengan implementasi berikut:

    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
            }
        }
    }
    

    Dari objek context kita dapat mengakses titik masuk kompilasi, atau metode Main. Instans mainMethod adalah IMethodSymbol, dan mewakili metode atau simbol seperti metode (termasuk konstruktor, destruktor, operator, atau pengakses properti/kejadian). Metode Microsoft.CodeAnalysis.Compilation.GetEntryPoint mengembalikan IMethodSymbol untuk titik masuk program. Metode lain memungkinkan Anda untuk menemukan simbol metode apa pun dalam sebuah proyek. Dari objek ini, kita dapat menjelaskan tentang namespace layanan yang berisi (jika ada) dan jenisnya. source dalam contoh ini adalah string yang diinterpolasi yang membuat template kode sumber yang akan dihasilkan, di mana lubang yang diinterpolasi diisi dengan namespace layanan yang berisi dan informasi jenis. source ditambahkan ke context dengan nama petunjuk. Untuk contoh ini, generator membuat file sumber baru yang berisi implementasi metode partial dalam aplikasi konsol. Anda dapat menulis generator sumber untuk menambahkan sumber apa pun yang Anda inginkan.

    Tip

    Parameter hintName dari metode GeneratorExecutionContext.AddSource dapat berupa nama unik apa pun. Sangat umum untuk menyediakan ekstensi file C# eksplisit seperti ".g.cs" atau ".generated.cs" untuk namanya. Nama file membantu mengidentifikasi file sebagai sumber yang dihasilkan.

  7. Sekarang kita memiliki generator yang telah berfungsi, tetapi perlu menghubungkannya ke aplikasi konsol kita. Edit proyek aplikasi konsol asli dan tambahkan berikut ini, ganti jalur proyek dengan yang dari proyek .NET Standar yang Anda buat di atas:

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

    Referensi baru ini bukan referensi proyek tradisional, dan harus diedit secara manual untuk menyertakan atribut OutputItemType dan ReferenceOutputAssembly. Untuk informasi selengkapnya tentang atribut OutputItemType dan ReferenceOutputAssemblyProjectReference, lihat Item proyek MSBuild umum: ProjectReference.

  8. Sekarang, ketika Anda menjalankan aplikasi konsol, Anda akan melihat bahwa kode yang dihasilkan telah dijalankan dan dicetak ke layar. Aplikasi konsol itu sendiri tidak mengimplementasikan metode HelloFrom, melainkan sumber yang dihasilkan selama kompilasi dari proyek Generator Sumber. Teks berikut adalah contoh output dari jendela konsol klien:

    Generator says: Hi from 'Generated Code'
    

    Catatan

    Anda mungkin perlu hidupkan ulang Visual Studio untuk melihat IntelliSense dan menghapus kesalahan karena pengalaman peralatan secara aktif ditingkatkan.

  9. Jika Anda menggunakan Visual Studio, Anda dapat melihat file yang dihasilkan sumber. Dari jendela Penjelajah Solusi, perluas Dependencies>Analyzers>SourceGenerator>SourceGenerator.HelloSourceGenerator , dan klik dua kali file Program.g.cs.

    Visual Studio: File yang dihasilkan sumber Penjelajah Solusi.

    Ketika Anda membuka file yang dihasilkan ini, Visual Studio akan menunjukkan bahwa file dibuat secara otomatis dan tidak dapat diedit.

    Visual Studio: File Program.g.cs yang dibuat secara otomatis.

  10. Anda juga dapat mengatur properti build untuk menyimpan file yang dihasilkan dan mengontrol tempat penyimpanan file yang dihasilkan. Di file proyek aplikasi konsol, tambahkan elemen <EmitCompilerGeneratedFiles> ke <PropertyGroup>, dan atur nilainya ke true. Bangun kembali proyek Anda. Sekarang, file yang dihasilkan dibuat di bawah obj/Debug/net6.0/generated/SourceGenerator/SourceGenerator.HelloSourceGenerator. Komponen peta jalur ke konfigurasi build, bingkai target, nama proyek generator sumber, dan nama jenis generator yang sepenuhnya memenuhi syarat. Anda dapat memilih folder output yang lebih nyaman dengan menambahkan elemen <CompilerGeneratedFilesOutputPath> ke file proyek aplikasi.

Langkah berikutnya

Cookbook Generator Sumber membahas beberapa contoh berikut dengan beberapa pendekatan yang disarankan untuk menyelesaikannya. Selain itu, kami memiliki sekumpulan sampel yang tersedia di GitHub yang dapat Anda coba sendiri.

Anda dapat mempelajari lebih lanjut tentang Generator Sumber di artikel ini: