Generátory zdrojů

Tento článek obsahuje přehled generátorů zdrojů, které se dodává jako součást sady .NET Compiler Platform ("Roslyn") SDK. Zdrojové generátory umožňují vývojářům jazyka C# kontrolovat uživatelský kód při kompilaci. Generátor může průběžně vytvářet nové zdrojové soubory jazyka C#, které se přidají do kompilace uživatele. Tímto způsobem máte kód, který se spouští během kompilace. Kontroluje program, aby vytvořil další zdrojové soubory, které se kompilují společně se zbytkem kódu.

Zdrojový generátor je nový druh komponenty, kterou můžou vývojáři v jazyce C# psát a která vám umožní dělat dvě hlavní věci:

  1. Načte objekt kompilace , který představuje veškerý zkompilovaný uživatelský kód. Tento objekt je možné zkontrolovat a můžete napsat kód, který pracuje se syntaxí a sémantickými modely pro zkompilovaný kód, stejně jako dnes s analyzátory.

  2. Generování zdrojových souborů jazyka C#, které lze během kompilace přidat do objektu kompilace. Jinými slovy, během kompilace můžete jako vstup do kompilace zadat další zdrojový kód.

Když je zkombinujeme, jsou tyto dvě věci to, co dělá zdrojové generátory tak užitečnými. Uživatelský kód můžete zkontrolovat se všemi bohatými metadaty, která kompilátor vytváří během kompilace. Generátor pak vygeneruje kód jazyka C# zpět do stejné kompilace, která je založená na analyzovaných datech. Pokud znáte analyzátory Roslyn, můžete si generátory zdrojů představit jako analyzátory, které můžou generovat zdrojový kód jazyka C#.

Zdrojové generátory běží jako fáze kompilace vizualizovaná níže:

Obrázek popisující různé části generování zdroje

Zdrojový generátor je sestavení .NET Standard 2.0, které kompilátor načítá společně s libovolnými analyzátory. Je použitelný v prostředích, kde je možné načíst a spustit komponenty .NET Standard.

Důležité

V současné době lze jako generátory zdrojů používat pouze sestavení .NET Standard 2.0.

Obvyklé scénáře

Existují tři obecné přístupy ke kontrole uživatelského kódu a generování informací nebo kódu na základě analýzy, kterou dnes používají technologie:

  • Reflexe modulu runtime.
  • Žonglování úloh NÁSTROJE MSBuild.
  • Tkaní zprostředkujícího jazyka (IL) (není popsáno v tomto článku).

Generátory zdrojů mohou být vylepšením oproti každému přístupu.

Reflexe modulu runtime

Reflexe modulu runtime je výkonná technologie, která byla do .NET přidána před dlouhou dobou. Existuje bezpočet scénářů pro jeho použití. Běžným scénářem je provedení analýzy uživatelského kódu při spuštění aplikace a použití těchto dat k vygenerování položek.

Například ASP.NET Core používá reflexi při prvním spuštění webové služby ke zjišťování konstruktorů, které jste definovali, aby bylo možné "připojit" věci, jako jsou kontrolery a stránky břitvy. I když to umožňuje psát přímočarý kód s výkonnými abstrakcemi, za běhu to znamená snížení výkonu: když se webová služba nebo aplikace poprvé spustí, nemůže přijímat žádné požadavky, dokud se nedokončí všechny kódy reflexe modulu runtime, které zjišťují informace o vašem kódu. I když tato penalizace výkonu není enormní, je to poněkud pevná cena, kterou nemůžete zlepšit ve své vlastní aplikaci.

U zdrojového generátoru může fáze zjišťování kontroleru při spuštění probíhat místo toho v době kompilace. Generátor může analyzovat zdrojový kód a vygenerovat kód, který potřebuje k "připojení" vaší aplikace. Použití zdrojových generátorů může mít za následek rychlejší spouštění, protože akce probíhající dnes za běhu by mohla být vložena do doby kompilace.

Žonglování úloh NÁSTROJE MSBuild

Generátory zdrojů můžou zvýšit výkon způsobem, který není omezen na reflexi za běhu, aby se zjistily i typy. Některé scénáře zahrnují vícenásobné volání úlohy nástroje MSBuild C# (označované jako CSC), aby bylo možné zkontrolovat data z kompilace. Jak si můžete představit, volání kompilátoru více než jednou ovlivňuje celkovou dobu potřebnou k sestavení aplikace. Zkoumáme, jak se generátory zdrojů dají použít k obejití potřeby žonglování s úlohami NÁSTROJE MSBuild, jako je tento, protože generátory zdrojů nenabízí jen určité výhody z výkonu, ale také umožňují nástrojům pracovat na správné úrovni abstrakce.

Další možností, kterou mohou generátory zdrojů nabídnout, je obviňování používání některých rozhraní API typu řetězec, například jak funguje ASP.NET Core směrování mezi řadiči a stránkami razor. U generátoru zdrojů lze směrování silně zadat s potřebnými řetězci, které se vygenerují jako podrobnosti v době kompilace. Tím se sníží počet chybných řetězcových literálů, které vedou k tomu, že požadavek nenarazí na správný kontroler.

Začínáme se zdrojovými generátory

V této příručce prozkoumáte vytvoření generátoru zdrojů pomocí ISourceGenerator rozhraní API.

  1. Vytvořte konzolovou aplikaci .NET. Tento příklad používá .NET 6.

  2. Program Nahraďte třídu následujícím kódem. Následující kód nepoužívá příkazy nejvyšší úrovně. Klasický formulář je povinný, protože tento první zdrojový generátor zapíše částečnou metodu v této Program třídě:

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

    Poznámka

    Tuto ukázku můžete spustit tak, jak je, ale zatím se nic nestane.

  3. Dále vytvoříme projekt generátoru zdrojů, který implementuje partial void HelloFrom protějšek metody.

  4. Vytvořte projekt standardní knihovny .NET, který cílí na cílovou architekturu netstandard2.0 moniker (TFM). Přidejte balíčky NuGet Microsoft.CodeAnalysis.Analyzers a 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

    Projekt zdrojového generátoru musí cílit na netstandard2.0 TFM, jinak nebude fungovat.

  5. Vytvořte nový soubor jazyka C# s názvem HelloSourceGenerator.cs , který určuje vlastní zdrojový generátor takto:

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

    Zdrojový generátor musí implementovat Microsoft.CodeAnalysis.ISourceGenerator rozhraní i mít .Microsoft.CodeAnalysis.GeneratorAttribute Ne všechny zdrojové generátory vyžadují inicializaci, což je případ této ukázkové implementace , kde ISourceGenerator.Initialize je prázdná.

  6. ISourceGenerator.Execute Obsah metody nahraďte následující implementací:

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

    Z objektu context můžeme získat přístup k vstupnímu bodu kompilace nebo Main metodě. Instance mainMethod je IMethodSymbola představuje metodu nebo symbol podobný metodě (včetně konstruktoru, destruktoru, operátoru nebo přistupujícího objektu vlastnosti/události). Metoda Microsoft.CodeAnalysis.Compilation.GetEntryPoint vrátí IMethodSymbol hodnotu pro vstupní bod programu. Jiné metody umožňují najít libovolný symbol metody v projektu. Z tohoto objektu můžeme zjistit, který obor názvů obsahuje (pokud existuje) a typ. V source tomto příkladu je interpolovaný řetězec, který šablonuje zdrojový kód, který se má vygenerovat, kde interpolované díry jsou vyplněny informacemi o oboru názvů a typu, které obsahují. Přidá source se do context pole s názvem nápovědy. V tomto příkladu generátor vytvoří nový vygenerovaný zdrojový soubor, který obsahuje implementaci partial metody v konzolové aplikaci. Můžete psát generátory zdrojů a přidávat libovolný zdroj, který chcete.

    Tip

    Parametr hintName z GeneratorExecutionContext.AddSource metody může být libovolný jedinečný název. Pro název je běžné zadat explicitní příponu souboru jazyka C#, například ".g.cs" nebo ".generated.cs" . Název souboru pomáhá identifikovat soubor jako vygenerovaný zdroj.

  7. Teď máme funkční generátor, ale potřebujeme ho připojit k naší konzolové aplikaci. Upravte původní projekt konzolové aplikace a přidejte následující a nahraďte cestu k projektu z projektu .NET Standard, který jste vytvořili výše:

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

    Tento nový odkaz není tradiční odkaz na projekt a je nutné ho ručně upravit, aby obsahoval OutputItemType atributy a ReferenceOutputAssembly . Další informace o atributech OutputItemTypeProjectReferencea ReferenceOutputAssembly nástroje najdete v tématu Běžné položky projektu MSBuild: ProjectReference.

  8. Když teď spustíte konzolovou aplikaci, měli byste vidět, že se vygenerovaný kód spustí a vytiskne se na obrazovku. Samotná konzolová aplikace metodu HelloFrom neimplementuje, místo toho se jedná o zdroj vygenerovaný během kompilace z projektu Source Generator. Následující text je příkladem výstupu z aplikace:

    Generator says: Hi from 'Generated Code'
    

    Poznámka

    Možná budete muset restartovat Visual Studio, abyste viděli IntelliSense a zbavili se chyb, protože se prostředí nástrojů aktivně vylepšuje.

  9. Pokud používáte Visual Studio, můžete zobrazit zdrojové vygenerované soubory. V okně Průzkumník řešení rozbalte položku Dependencies>Analyzers>SourceGenerator>SourceGenerator.HelloSourceGenerator a poklikejte na soubor Program.g.cs.

    Visual Studio: Průzkumník řešení zdrojové vygenerované soubory.

    Při otevření tohoto vygenerovaného souboru sada Visual Studio označí, že je soubor automaticky vygenerovaný a že ho nelze upravit.

    Visual Studio: Automaticky vygenerovaný soubor Program.g.cs.

  10. Můžete také nastavit vlastnosti sestavení pro uložení vygenerovaného souboru a určit, kam se vygenerované soubory ukládají. Do souboru projektu konzolové aplikace přidejte element do objektu <EmitCompilerGeneratedFiles><PropertyGroup>a nastavte jeho hodnotu na true. Znovu sestavte projekt. Nyní se vygenerované soubory vytvoří pod obj/Debug/net6.0/generated/SourceGenerator/SourceGenerator.HelloSourceGenerator. Komponenty mapování cesty ke konfiguraci sestavení, cílové architektuře, názvu projektu generátoru zdroje a plně kvalifikovanému názvu typu generátoru. Pohodlnější výstupní složku můžete zvolit přidáním elementu <CompilerGeneratedFilesOutputPath> do souboru projektu aplikace.

Další kroky

V příručce Source Generators Cookbook najdete některé z těchto příkladů s některými doporučenými přístupy k jejich řešení. Kromě toho máme na GitHubu k dispozici sadu ukázek , které si můžete vyzkoušet sami.

Další informace o zdrojových generátorech najdete v těchto článcích: