Megosztás a következőn keresztül:


Forrásgenerátorok

Ez a cikk áttekintést nyújt a .NET Fordítóplatform ("Roslyn") SDK részeként szállított forrásgenerátorokról. A forrásgenerátorok lehetővé teszik a C#-fejlesztők számára, hogy a fordítás során megvizsgálják a felhasználói kódot. A generátor menet közben létrehozhat új C#-forrásfájlokat, amelyeket hozzáad a felhasználó fordításához. Ily módon olyan kóddal rendelkezik, amely a fordítás során fut. Megvizsgálja a programot, hogy további forrásfájlokat állítson elő, amelyek a kód többi részével együtt vannak lefordítva.

A Forrásgenerátor egy új típusú összetevő, amelyet a C#-fejlesztők írhatnak, és két fő dolgot tehetnek:

  1. Egy fordítási objektum lekérése , amely az összes lefordított felhasználói kódot jelöli. Ez az objektum megvizsgálható, és írhat olyan kódot, amely a lefordított kód szintaxisával és szemantikai modelljeivel működik, akárcsak a mai elemzők esetében.

  2. Olyan C#-forrásfájlok létrehozása, amelyek összeállítás közben hozzáadhatók egy fordítási objektumhoz. Más szóval további forráskódot is megadhat egy fordítás bemeneteként a kód fordítása közben.

Ha kombináljuk, ez a két dolog teszi olyan hasznossá a Forrásgenerátorokat. A fordító által a fordítás során létrehozott összes gazdag metaadattal megvizsgálhatja a felhasználói kódot. A generátor ezután visszabocsátja a C#-kódot ugyanabba a fordításba, amely az ön által elemezett adatokon alapul. Ha ismeri a Roslyn-elemzőket, a Forrásgenerátorok olyan elemzőkként is felfoghatók, amelyek C#-forráskódot bocsáthatnak ki.

A forrásgenerátorok az alábbiakban vizualizált fordítási fázisként futnak:

A forráslétrehozás különböző részeit leíró ábra

A Forrásgenerátor egy .NET Standard 2.0-s szerelvény, amelyet a fordító tölt be minden elemzővel együtt. Olyan környezetekben használható, ahol a .NET Standard összetevők betölthetők és futtathatók.

Fontos

Jelenleg csak a .NET Standard 2.0 szerelvények használhatók forrásgenerátorként.

Gyakori forgatókönyvek

A felhasználói kód vizsgálatának és a technológiák által használt elemzésen alapuló információk vagy kódok előállításának három általános megközelítése van:

  • Futásidejű tükröződés.
  • MsBuild-feladatok zsonglőrítése.
  • Köztes nyelv (IL) szövés (ez a cikk nem foglalkozik).

A forrásgenerátorok az egyes megközelítésekhez képest javulást jelenthetnek.

Futásidejű tükröződés

A futtatókörnyezeti tükröződés egy hatékony technológia, amelyet már régen hozzáadtak a .NET-hez. Számtalan forgatókönyv létezik a használatához. Gyakori forgatókönyv a felhasználói kód elemzésének végrehajtása, amikor egy alkalmazás elindul, és az adatok felhasználásával hoz létre dolgokat.

Például ASP.NET Core tükröződést használ, amikor a webszolgáltatás először futtatja az Ön által definiált szerkezeteket, hogy "bekenhesse" a vezérlőket és a borotvalapokat. Bár ez lehetővé teszi, hogy hatékony absztrakciókkal egyszerű kódot írjon, futásidőben teljesítménybírságot von maga után: amikor a webszolgáltatás vagy az alkalmazás először elindul, nem fogad el kéréseket, amíg az összes olyan futtatókörnyezeti tükröződési kód le nem fut, amely információkat derít fel a kódról. Bár ez a teljesítménybírság nem hatalmas, ez némileg rögzített költség, amelyet nem tud javítani saját alkalmazásában.

Forrásgenerátor esetén az indítás vezérlőfelderítési fázisa a fordítási időpontban történhet. A generátor elemezheti a forráskódot, és kibocsáthatja azt a kódot, amelyre szüksége van az alkalmazás "beírásához". A forrásgenerátorok használata gyorsabb indítási időket eredményezhet, mivel egy ma futó művelet leküldhető a fordítási időpontba.

MsBuild feladatok zsonglőrítése

A forrásgenerátorok olyan módokon javíthatják a teljesítményt, amelyek nem korlátozódnak a futásidőben történő tükröződésre a típusok felderítése érdekében. Egyes esetekben az MSBuild C#-feladat (CSC) többszöri meghívásával vizsgálhatják meg a fordításból származó adatokat. Képzelheti, hogy a fordító többszöri meghívása hatással van az alkalmazás létrehozásához szükséges teljes időre. Azt vizsgáljuk, hogy a Forrásgenerátorok hogyan használhatók az MSBuild-feladatok zsonglőrítésének szükségességének enyhítésére, mivel a forrásgenerátorok nem csupán néhány teljesítményelőnyt kínálnak, hanem lehetővé teszik az eszközök megfelelő absztrakciós szinten való működését is.

A forrásgenerátorok egy másik képességet is kínálhatnak, ha nem használják a "sztringgel begépelt" API-kat, például azt, hogy hogyan működik ASP.NET Core vezérlők és razor-lapok közötti útválasztás. Forrásgenerátor esetén az útválasztás erősen beírható úgy, hogy a szükséges sztringek fordítási idő részleteiként jönnek létre. Ez csökkenti, hogy egy helytelenül beírt sztringkonstans hányszor vezet olyan kéréshez, amely nem éri el a megfelelő vezérlőt.

A forrásgenerátorok használatának első lépései

Ebben az útmutatóban megismerkedhet egy forrásgenerátor API-val történő ISourceGenerator létrehozásával.

  1. Hozzon létre egy .NET-konzolalkalmazást. Ez a példa a .NET 6-ot használja.

  2. Cserélje le az osztályt Program a következő kódra. Az alábbi kód nem használ legfelső szintű utasításokat. A klasszikus űrlapra azért van szükség, mert ez az első forrásgenerátor részleges metódust ír ebben az Program osztályban:

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

    Megjegyzés

    A minta futtatható állapotban, de még semmi sem fog történni.

  3. Ezután létrehozunk egy forrásgenerátorprojektet, amely implementálja a partial void HelloFrom metódus megfelelőjét.

  4. Hozzon létre egy standard .NET-kódtárprojektet, amely a netstandard2.0 cél-keretrendszer monikerét (TFM) célozza meg. Adja hozzá a Microsoft.CodeAnalysis.Analyzers és a Microsoft.CodeAnalysis.CSharp NuGet-csomagokat:

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

    Tipp

    A forrásgenerátor projektnek meg kell céloznia a netstandard2.0 TFM-et, különben nem fog működni.

  5. Hozzon létre egy új C#-fájlt HelloSourceGenerator.cs néven, amely a saját forrásgenerátorát a következőképpen határozza meg:

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

    A forrásgenerátornak egyaránt implementálnia kell az interfészt Microsoft.CodeAnalysis.ISourceGenerator , és rendelkeznie kell a következővel Microsoft.CodeAnalysis.GeneratorAttribute: . Nem minden forrásgenerátor igényel inicializálást, és ez a példa implementáció esetében is így van – ahol ISourceGenerator.Initialize üres.

  6. Cserélje le a ISourceGenerator.Execute metódus tartalmát a következő implementációra:

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

    context Az objektumból hozzáférhetünk a fordítások belépési ponthoz vagy Main metódushoz. A mainMethod példány egy IMethodSymbol, és egy metódushoz vagy metódushoz hasonló szimbólumot jelöl (beleértve a konstruktort, a destruktort, az operátort vagy a tulajdonságot/esemény tartozékot). A Microsoft.CodeAnalysis.Compilation.GetEntryPoint metódus a IMethodSymbol program belépési pontjának értékét adja vissza. Más metódusok lehetővé teszik, hogy bármilyen metódusszimbólumot megtaláljon egy projektben. Ebből az objektumból meg tudjuk indokolni a tartalmazó névteret (ha van ilyen) és a típust. Ebben source a példában egy interpolált sztring található, amely a létrehozandó forráskódot sablonosíti, ahol az interpolált lyukak tele vannak a névteret tartalmazó és a típusadatokkal. A source hozzá van adva a context tipp nevéhez. Ebben a példában a generátor létrehoz egy új generált forrásfájlt, amely a partial metódus implementációját tartalmazza a konzolalkalmazásban. A forrásgenerátorok írásához bármilyen forrást hozzáadhat.

    Tipp

    A hintName metódus paramétere GeneratorExecutionContext.AddSource bármilyen egyedi név lehet. Gyakori, hogy explicit C#-fájlkiterjesztést ad meg, például ".g.cs" vagy ".generated.cs" a nevet. A fájlnév segít azonosítani a fájlt a létrehozott forrásként.

  7. Most már van egy működő generátorunk, de csatlakoztatnunk kell a konzolalkalmazáshoz. Szerkessze az eredeti konzolalkalmazás-projektet, és adja hozzá a következőt, és cserélje le a projekt elérési útját a fent létrehozott .NET Standard projektből származóra:

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

    Ez az új hivatkozás nem hagyományos projekthivatkozás, és manuálisan kell szerkeszteni a és ReferenceOutputAssembly az OutputItemType attribútumok belefoglalásához. További információ a OutputItemType és ReferenceOutputAssembly attribútumokról ProjectReference: Gyakori MSBuild projektelemek: ProjectReference.

  8. Most, amikor futtatja a konzolalkalmazást, látnia kell, hogy a létrehozott kód lefut, és a képernyőn jelenik meg. Maga a konzolalkalmazás nem implementálja a HelloFrom metódust, hanem a Forrásgenerátor projekt fordítása során létrehozott forrás. Az alábbi szöveg egy példakimenet az alkalmazásból:

    Generator says: Hi from 'Generated Code'
    

    Megjegyzés

    Előfordulhat, hogy újra kell indítania a Visual Studiót az IntelliSense megtekintéséhez és a hibák elhárításához, mivel az eszközhasználat aktívan javul.

  9. Ha Visual Studiót használ, láthatja a forrás által létrehozott fájlokat. A Megoldáskezelő ablakban bontsa ki a Dependencies>Analyzers>SourceGenerator>SourceGenerator.HelloSourceGenerator elemet, és kattintson duplán a Program.g.cs fájlra.

    Visual Studio: Megoldáskezelő által létrehozott fájlok.

    A létrehozott fájl megnyitásakor a Visual Studio jelzi, hogy a fájl automatikusan létrejön, és nem szerkeszthető.

    Visual Studio: Automatikusan létrehozott Program.g.cs fájl.

  10. A buildtulajdonságok beállításával mentheti a létrehozott fájlt, és szabályozhatja a létrehozott fájlok tárolási helyét. A konzolalkalmazás projektfájljában adja hozzá az elemet egyhez <EmitCompilerGeneratedFiles><PropertyGroup>, és állítsa az értékét a értékre true. Hozza létre újra a projektet. A létrehozott fájlok most az obj/Debug/net6.0/generated/SourceGenerator/SourceGenerator.HelloSourceGenerator területen jönnek létre. Az elérési út összetevői a buildkonfigurációhoz, a cél-keretrendszerhez, a forrásgenerátor projekt nevéhez és a generátor teljes típusnevéhez felelnek meg. A kényelmesebb kimeneti mappát úgy választhatja ki, hogy hozzáadja az <CompilerGeneratedFilesOutputPath> elemet az alkalmazás projektfájljába.

Következő lépések

A Source Generators Cookbook néhány ilyen példát mutat be, és néhány javasolt megközelítést alkalmaz a megoldásukhoz. Emellett rendelkezésre áll egy mintakészlet a GitHubon , amelyet önállóan is kipróbálhat.

A forrásgenerátorokról az alábbi cikkekben talál további információt: