स्वचालित रूप से कोड जनरेट करने के लिए Visual Studio और स्रोत जेनरेटर का उपयोग करें

पेज अद्यतन :
पेज निर्माण की तारीख :

परिचालन का वातावरण

विजुअल स्टूडियो
  • विजुअल स्टूडियो 2022
।जाल
  • .नेट 8.0

आवश्यकताएँ

विजुअल स्टूडियो
  • यह कुछ पुराने संस्करण के साथ भी काम करता है
।जाल
  • यह कुछ पुराने संस्करण के साथ भी काम करता है

पहले

अपनी खुद की परिभाषाओं के साथ स्वचालित रूप से कोड उत्पन्न करने के लिए कई तकनीकें हैं, लेकिन इस आलेख में मैं आपको दिखाऊंगा कि स्रोत जेनरेटर का उपयोग कैसे करें। स्रोत जनरेटर के सबसे बड़े लाभों में से एक यह है कि यह वर्तमान परियोजना के स्रोत कोड की संरचना का विश्लेषण करता है और इसके आधार पर नया कोड उत्पन्न करता है। उदाहरण के लिए, जब आप कोई नई कक्षा बनाते हैं, तो आप इसे बना सकते हैं ताकि कक्षा से मेल खाने के लिए कोड स्वचालित रूप से जुड़ जाए। आप प्रोग्राम कर सकते हैं कि आप किस प्रकार का कोड जनरेट करना चाहते हैं, ताकि आप अपनी पसंद का कोई भी स्वचालित कोड जनरेशन बना सकें।

कोड अनिवार्य रूप से पृष्ठभूमि में स्वतः उत्पन्न होता है और पर्दे के पीछे परियोजना में शामिल होता है। चूंकि यह एक दृश्यमान तरीके से फ़ाइल के रूप में आउटपुट नहीं है, इसलिए इसका उपयोग सामान्य उद्देश्यों के लिए ऑटो-जेनरेट किए गए कोड का पुन: उपयोग करने के उद्देश्य से नहीं किया जाता है (हालांकि इसे समय के लिए कॉपी करके हटाया जा सकता है)। हालांकि, चूंकि कोड स्वचालित रूप से परियोजना की संरचना के अनुसार उत्पन्न होता है, यह मैन्युअल इनपुट की लागत को कम करता है और तदनुसार कोड लेखन त्रुटियों को कम करता है, जो एक बड़ा लाभ है।

इस आलेख में, मैं समझाऊंगा कि कैसे जांचें कि कोड स्वचालित रूप से उत्पन्न होता है, इसलिए मैं वास्तव में कोड का गहराई से विश्लेषण करने और उन्नत आउटपुट करने के लिए इतनी दूर नहीं जाऊंगा। कृपया इसे स्वयं एक आवेदन के रूप में देखें।

सेटअप

सबसे पहले, Visual Studio स्थापित करें। निम्नलिखित युक्तियों में एक संक्षिप्त विवरण संक्षेप में दिया गया है।

मूल रूप से, आप इसे किसी भी प्रोजेक्ट के लिए उपयोग कर सकते हैं, इसलिए इससे कोई फर्क नहीं पड़ता कि आपने कौन सा वर्कलोड सेट किया है। हालांकि, इस बार, एक "व्यक्तिगत घटक" के रूप में, "। नेट कंपाइलर प्लेटफार्म एसडीके। यह स्रोत जेनरेटर विकास के दौरान डीबगिंग के लिए उपयोगी है। यदि आपके पास पहले से ही Visual Studio स्थापित है, तो आप इसे उपकरण और सुविधाएँ प्राप्त करें > के अंतर्गत Visual Studio मेनू से जोड़ सकते हैं.

स्रोत जनरेटर प्रोजेक्ट बनाना और तैयार करना

स्रोत जेनरेटर मुख्य अनुप्रयोग परियोजना से अलग एक परियोजना में बनाया गया है। इससे कोई फर्क नहीं पड़ता कि आप उन्हें पहले बनाते हैं या बाद में अतिरिक्त बनाते हैं। इस मामले में, मैं इसे स्रोत जेनरेटर परियोजना से बना देंगे.

नया प्रोजेक्ट बनाएं स्क्रीन पर, क्लास लाइब्रेरी चुनें।

परियोजना का नाम कुछ भी हो सकता है, लेकिन अभी CodeGenerator के लिए, हम इसे छोड़ देंगे .

कक्षा पुस्तकालयों के लिए, स्रोत जनरेटर वर्तमान में समर्थन करता है। नेट मानक 2.0।

एक बार जब आप अपना प्रोजेक्ट बना लेते हैं, तो NuGet के साथ Microsoft.CodeAnalysis.CSharp पैकेज प्राप्त करें। संस्करण के आधार पर व्यवहार भिन्न हो सकता है, लेकिन पुराने संस्करण का उपयोग जारी रखने का कोई मतलब नहीं है, इसलिए मैं नवीनतम संस्करण डालूंगा।

फिर प्रोजेक्ट फ़ाइल को कोड के रूप में खोलें।

जब आप इसे खोलेंगे, तो आप निम्नलिखित देखेंगे।

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
  </ItemGroup>

</Project>

इसे इस प्रकार जोड़ें: आपको जो कुछ भी चाहिए उसे जोड़ने के लिए स्वतंत्र महसूस करें।

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>

    <!-- Roslyn によるコード解析を行う -->
    <IsRoslynComponent>true</IsRoslynComponent>
    <!-- 入れないと警告が表示されるので入れておく -->
    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
    <!-- 最新の C# の記述を使いたいので入れておく -->
    <LangVersion>Latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
  </ItemGroup>

</Project>

इसके बाद, उस कक्षा के लिए कोड लिखें जो स्वचालित रूप से कोड उत्पन्न करेगा। सबसे पहले, हम केवल एक ढांचा बनाएंगे, इसलिए कृपया मौजूदा कोड को शुरुआत Class1.cs से फिर से लिखें या एक नया कोड जोड़ें।

कोड इस तरह दिखना चाहिए: कक्षा का नाम कुछ भी हो सकता है, लेकिन ऐसा नाम होना बेहतर है जो दिखाता है कि किस प्रकार का कोड स्वचालित रूप से उत्पन्न होता है। SampleGenerator अभी के लिए, इसे छोड़ दें . Initialize इस विधि में, आप कोड विश्लेषण और कोड जनरेशन प्रक्रिया लिखेंगे।

using Microsoft.CodeAnalysis;

namespace CodeGenerator
{
  [Generator(LanguageNames.CSharp)]
  public partial class SampleGenerator : IIncrementalGenerator
  {
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
    }
  }
}

इसके बाद, उस एप्लिकेशन के लिए एक प्रोजेक्ट बनाएं जिसके लिए आप स्वचालित रूप से कोड उत्पन्न करना चाहते हैं। यह कुछ भी हो सकता है, लेकिन यहां हम इसे कंसोल एप्लिकेशन के रूप में उपयोग करेंगे। प्रोजेक्ट के ढांचे का प्रकार और संस्करण जिससे कोड स्वचालित रूप से उत्पन्न होता है, सामान्य संख्या से मेल खाता है।

अनुप्रयोग-साइड प्रोजेक्ट से स्रोत जनरेटर प्रोजेक्ट का संदर्भ जोड़ें।

गुणों में कुछ सेटिंग्स सेट नहीं की जा सकती हैं, इसलिए प्रोजेक्ट फ़ाइल को कोड में खोलें।

मुझे लगता है कि यह इस तरह दिखता है:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\CodeGenerator\CodeGenerator.csproj" />
  </ItemGroup>

</Project>

संदर्भित प्रोजेक्ट के लिए सेटिंग्स निम्नानुसार जोड़ें: सुनिश्चित करें कि आप XML के सिंटैक्स में कोई ग़लती नहीं करते हैं.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\CodeGenerator\CodeGenerator.csproj">
      <OutputItemType>Analyzer</OutputItemType>
      <ReferenceOutputAssembly>False</ReferenceOutputAssembly>
    </ProjectReference>
  </ItemGroup>

</Project>

इसके बाद, "कोड जेनरेटर साइड" पर प्रोजेक्ट प्रॉपर्टी खोलें।

डीबग लॉन्च गुण UI खोलने के लिए लिंक क्लिक करें।

मूल प्रोफ़ाइल हटाएँ क्योंकि आप उसका उपयोग नहीं करना चाहते हैं.

नई प्रोफ़ाइल जोड़ें.

Roslyn कॉम्पोनेंट चुनें।

यदि आपने अब तक सेटिंग्स की हैं, तो आपको एप्लिकेशन प्रोजेक्ट का चयन करने में सक्षम होना चाहिए, इसलिए इसे चुनें।

यह तैयारी का अंत है।

जांचें कि क्या आप डीबग कर सकते हैं

स्रोत जनरेटर कोड खोलें और Initialize विधि के अंत में एक ब्रेकपॉइंट रखें।

आइए स्रोत जनरेटर को डीबग करें।

यदि प्रक्रिया विराम बिंदु पर बंद हो जाता है, तो आप पुष्टि कर सकते हैं कि आप सामान्य रूप से डीबग कर रहे हैं। इससे आपके स्रोत जनरेटर का विकास काफी आसान हो जाना चाहिए।

कुछ समय के लिए, आइए एक निश्चित कोड आउटपुट करें

सबसे पहले, आइए एक निश्चित कोड को आसानी से आउटपुट करने का प्रयास करें। यह आसान है क्योंकि आपको कोड का विश्लेषण करने की भी आवश्यकता नहीं है। यहां तक कि अगर यह एक निश्चित कोड है, तो इसे एक स्ट्रिंग के रूप में संभाला जाता है, इसलिए प्रोग्राम बनाकर एक निश्चित रूप में कोड के उत्पादन को बढ़ाना संभव है।

यदि आप एक निश्चित कोड आउटपुट करना चाहते हैं, तो आप context.RegisterPostInitializationOutput . निम्नलिखित कोड आउटपुट का एक उदाहरण है।

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;

namespace CodeGenerator;

[Generator(LanguageNames.CSharp)]
public partial class SampleGenerator : IIncrementalGenerator
{
  public void Initialize(IncrementalGeneratorInitializationContext context)
  {
    // コードを解析せずそのままコードを出力するならこれを使用します
    context.RegisterPostInitializationOutput(static postInitializationContext =>
    {
      // コード生成処理が中断されることもあるので CancellationToken 処理は入れたほうがいいです
      System.Threading.CancellationToken token = postInitializationContext.CancellationToken;
      token.ThrowIfCancellationRequested();

      // 出力するコードを作ります
      var source = """
internal static class SampleClass
{
  public static void Hello() => Console.WriteLine("Hello Source Generator!!");
}
""";
      
      // コードファイルに出力します (実際に目に見えるどこかのフォルダに出力されるわけではありません)
      postInitializationContext.AddSource("SampleGeneratedFile.cs", SourceText.From(source, Encoding.UTF8));
    });
  }
}

कोड की सामग्री टिप्पणियों में लिखी गई है, इसलिए मैं विवरण छोड़ दूंगा। इसे बनाएं और सुनिश्चित करें कि कोई त्रुटि नहीं है।

बिल्ड पूरा होने पर, आप कोड जनरेटर द्वारा जनरेट किए गए कोड को देखने के लिए एप्लिकेशन प्रोजेक्ट में "विश्लेषक" का विस्तार कर सकते हैं।

यदि आप इसे नहीं देखते हैं, तो Visual Studio को पुनरारंभ करें और इसे जाँचें। ऐसा लगता है कि इस समय, Visual Studio अद्यतन नहीं किया जा सकता है जब तक आप इसे पुनरारंभ करें। प्रोग्राम बनाते समय उपयोग किए जाने वाले इंटेलिसेंस और सिंटैक्स हाइलाइटिंग समान हैं। हालांकि, चूंकि कोड स्वयं निर्माण के समय परिलक्षित होता है, ऐसा लगता है कि कार्यक्रम आवेदन पक्ष पर परिलक्षित होता है।

एक बार कोड उत्पन्न हो जाने के बाद, इसे अपने एप्लिकेशन में उपयोग करने का प्रयास करें। यह ठीक काम करना चाहिए।

कोड का विश्लेषण और जनरेट करें

यदि आप कोड को सामान्य रूप से संसाधित करते हैं और इसे आउटपुट करते हैं, तो यह अन्य स्वचालित कोड पीढ़ी से बहुत अलग नहीं है, और आप स्रोत जनरेटर के लाभों का लाभ नहीं उठा सकते हैं। तो अब एप्लिकेशन प्रोजेक्ट के कोड का विश्लेषण करें और तदनुसार कोड जनरेट करें।

हालांकि, कोड का विश्लेषण काफी गहरा है, इसलिए मैं यहां सब कुछ समझा नहीं सकता। कुछ समय के लिए, मैं उस बिंदु तक समझाऊंगा जहां आप कोड का विश्लेषण और आउटपुट कर सकते हैं। यदि आप गहराई में जाना चाहते हैं, तो कृपया अपना खुद का शोध करें।

कुछ समय के लिए, एक उदाहरण के रूप में, मैं स्वचालित रूप से कोड बनाना चाहता हूं "सभी बनाई गई कक्षाओं में Reset एक विधि जोड़ें और सभी गुणों के मूल्य default को रीसेट करें"। हाल के वर्षों में, ऐसा लगता है कि अपरिवर्तनीय उदाहरणों को संभालना पसंद किया गया है, लेकिन गेम कार्यक्रमों में, हर फ्रेम में एक नया उदाहरण बनाना संभव नहीं है, इसलिए मुझे लगता है कि मूल्य को रीसेट करने की प्रक्रिया के लिए एक उपयोग है। default आप इसके अलावा कुछ और सेट करना चाह सकते हैं, लेकिन यदि आप ऐसा करते हैं, तो यह एक लंबा पहला उदाहरण होगा, इसलिए कृपया इसे स्वयं लागू करें।

कोड जेनरेटर साइड

आपके द्वारा पहले बनाए गए जनरेटर को रखने के उद्देश्य से एक नई कक्षा बनाएं। यदि स्वचालित रूप से उत्पन्न होने वाली सामग्री बदल जाती है, तो किसी अन्य वर्ग में एक नया बनाना बेहतर होता है।

Roslyn कंपाइलर कोड की संरचना का ख्याल रखता है, इसलिए हम यहाँ जो करने जा रहे हैं वह संरचित डेटा को पार्स करना और कोड लिखना है।

सबसे पहले, मैं सभी कोड पोस्ट करूंगा। मैंने जितना संभव हो उतना कम कोड रखने की कोशिश की है। इस बार यह काम करता है, लेकिन यह केवल एक स्तर पर है जो श्री/सुश्री के रूप में चलता है, इसलिए जब आप वास्तव में विकास के साथ आगे बढ़ते हैं, तो कुछ लापता हिस्से होंगे। कृपया आवश्यकतानुसार वहां सुधार करें।

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;
using System.Text;
using System.Threading;

namespace CodeGenerator;

[Generator(LanguageNames.CSharp)]
public partial class Sample2Generator : IIncrementalGenerator
{
  public void Initialize(IncrementalGeneratorInitializationContext context)
  {
    // 構文を解析し処理対象のノードをコード出力用に変換します
    var syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(
      // すべてのノードが処理対象となるため、predicate で処理対象とするノードを限定します
      predicate: static (node, cancelToken) =>
      {
        // 処理が中断される場面は多々あるのでいつでもキャンセルできるようにしておく
        cancelToken.ThrowIfCancellationRequested();

        // クラスのノードのみを対象とする (record とかつけると別な種類のノードになるので注意)
        // また partial class であること
        return node is ClassDeclarationSyntax nodeClass && nodeClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
      },
      // 対象のノードをコード出力に必要な形に変換します
      transform: static (contextSyntax, cancelToken) =>
      {
        // いつでもキャンセルできるようにしておく
        cancelToken.ThrowIfCancellationRequested();

        // 今回は ClassDeclarationSyntax 確定なのでそのままキャストします
        ClassDeclarationSyntax classDecl = (ClassDeclarationSyntax)contextSyntax.Node;

        // データと構文から宣言されたシンボルを取得します
        // 今回利用場面がほとんどないですが取得しておくといろんな情報を取得できるので便利です
        var symbol = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(contextSyntax.SemanticModel, classDecl, cancelToken)!;

        // 出力に必要な値を返します。大抵の型を渡すことができます
        return (symbol, classDecl);
      }
    );

    // 解析し変換した情報をもとにコードを出力します
    context.RegisterSourceOutput(syntaxProvider, static (contextSource, input) =>
    {
      // いつでもキャンセルできるようにしておく
      CancellationToken cancelToken = contextSource.CancellationToken;
      cancelToken.ThrowIfCancellationRequested();

      // 解析後に渡された値を受け取ります
      var (symbol, classDecl) = input;

      StringBuilder sbInitValues = new();

      // プロパティを列挙して値を初期化するコードを生成
      foreach (var syntax in classDecl.Members.OfType<PropertyDeclarationSyntax>())
      {
        sbInitValues.AppendLine($"{syntax.Identifier.ValueText} = default;");
      }

      // 出力するコードを作成
      var source = @$"
        {string.Join(" ", classDecl.Modifiers.Select(x => x.ToString()))} class {symbol.Name}
        {{
          public void Reset()
          {{
            {sbInitValues}
          }}
        }}";

      // ファイル名はクラス名に合わせておきます。作成したコードを出力します
      contextSource.AddSource($"{symbol.Name}.g.cs", source);
    });
  }
}

मैंने इसके बारे में टिप्पणियों में लिखा है, लेकिन मैं इसे कुछ स्थानों पर समझाऊंगा।

// 構文を解析し処理対象のノードをコード出力用に変換します
var syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(
  // すべてのノードが処理対象となるため、predicate で処理対象とするノードを限定します
  predicate: ...,
  // 対象のノードをコード出力に必要な形に変換します
  transform: ...
);

context.SyntaxProvider.CreateSyntaxProvider एप्लिकेशन प्रोजेक्ट में कोड का विश्लेषण करने और इसे यथासंभव संरचना करने के लिए। सब कुछ नोड्स में विभाजित है, और हम यह निर्धारित करने के लिए उपयोग करते हैं कि हम predicate उनमें से किसे संसाधित करना चाहते हैं। इसके अलावा, यदि आवश्यक हो transform , संसाधित वस्तु को परिवर्तित करें और इसे आउटपुट पक्ष में पास करें।

predicate इस मामले में, हम निम्नलिखित कार्य कर रहे हैं।

// すべてのノードが処理対象となるため、predicate で処理対象とするノードを限定します
predicate: static (node, cancelToken) =>
{
  // 処理が中断される場面は多々あるのでいつでもキャンセルできるようにしておく
  cancelToken.ThrowIfCancellationRequested();

  // クラスのノードのみを対象とする (record とかつけると別な種類のノードになるので注意)
  // また partial class であること
  return node is ClassDeclarationSyntax nodeClass && nodeClass.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword));
},

सबसे पहले, किसी भी प्रक्रिया के साथ, स्वचालित रूप से कोड उत्पन्न करने की प्रक्रिया हमेशा समय से पहले रद्द की जा सकती है। इसलिए कॉल करने के लिए ThrowIfCancellationRequested कैंसलेशन टोकन का उपयोग करें ताकि आप किसी भी समय इंटरप्ट कर सकें।

predicate अब जब प्रत्येक नोड को कॉल किया जाता है, तो हम यह निर्धारित करना चाहते हैं कि हम कौन सा संसाधित करना चाहते हैं। चूंकि उनमें से एक बड़ी संख्या है, इसलिए उन्हें कुछ हद तक यहां कम करना बेहतर है।

चूंकि हम इस बार कक्षा में प्रसंस्करण जोड़ने जा रहे हैं, इसलिए हम ClassDeclarationSyntax यह निर्धारित करेंगे कि क्या यह है और यह निर्धारित करेगा कि केवल कक्षा संसाधित की जाएगी या नहीं। partial class इसके अलावा, चूंकि कोड के साथ संलग्न है, इसलिए इसे partial class एक निर्णय के रूप में रखा गया है।

// 対象のノードをコード出力に必要な形に変換します
transform: static (contextSyntax, cancelToken) =>
{
  // いつでもキャンセルできるようにしておく
  cancelToken.ThrowIfCancellationRequested();

  // 今回は ClassDeclarationSyntax 確定なのでそのままキャストします
  ClassDeclarationSyntax classDecl = (ClassDeclarationSyntax)contextSyntax.Node;

  // データと構文から宣言されたシンボルを取得します
  // 今回利用場面がほとんどないですが取得しておくといろんな情報を取得できるので便利です
  var symbol = Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(contextSyntax.SemanticModel, classDecl, cancelToken)!;

  // 出力に必要な値を返します。大抵の型を渡すことができます
   return (symbol, classDecl);
}

transform आपको विश्लेषण को आवश्यक रूप में बदलने और इसे कोड आउटपुट में पास करने की अनुमति देता है। इस बार, हम बहुत अधिक रूपांतरण नहीं करते हैं, लेकिन आउटपुट पक्ष को मान पास करते हैं जैसा कि यह है return । हमें रास्ते में "घोषित प्रतीक" मिल रहे हैं, लेकिनSemanticModel हम बहुत सारी अतिरिक्त जानकारी प्राप्त कर सकते हैं और Syntac , इसलिए मुझे लगता है कि यदि आवश्यक हो तो हम इसे प्राप्त कर सकते हैं। return टुपल्स बनाए और लौटाए जाते हैं, लेकिन आउटपुट साइड को पास किए जाने वाले डेटा का कॉन्फ़िगरेशन कुछ भी हो सकता है। यदि आप कई डेटा पास करना चाहते हैं, तो आप इस तरह एक टपल बना सकते हैं, या आप अपनी खुद की कक्षा को परिभाषित कर सकते हैं और इसे पास कर सकते हैं।

// 解析し変換した情報をもとにコードを出力します
context.RegisterSourceOutput(syntaxProvider, static (contextSource, input) =>
{
  // いつでもキャンセルできるようにしておく
  CancellationToken cancelToken = contextSource.CancellationToken;
  cancelToken.ThrowIfCancellationRequested();

  // 解析後に渡された値を受け取ります
  var (symbol, classDecl) = input;

  StringBuilder sbInitValues = new();

  // プロパティを列挙して値を初期化するコードを生成
  foreach (var syntax in classDecl.Members.OfType<PropertyDeclarationSyntax>())
  {
    sbInitValues.AppendLine($"{syntax.Identifier.ValueText} = default;");
  }

  // 出力するコードを作成
  var source = @$"
    {string.Join(" ", classDecl.Modifiers.Select(x => x.ToString()))} class {symbol.Name}
    {{
      public void Reset()
      {{
        {sbInitValues}
      }}
    }}";

  // ファイル名はクラス名に合わせておきます。作成したコードを出力します
  contextSource.AddSource($"{symbol.Name}.g.cs", source);
});

context.RegisterSourceOutputcontext.SyntaxProvider.CreateSyntaxProvider अब, हम द्वारा संसाधित किए जाने वाले डेटा के आधार पर कोड उत्पन्न और आउटपुट करेंगे। context.SyntaxProvider.CreateSyntaxProvider return का मान दूसरे तर्क के रूप में प्राप्त किया जा सकता है Action , इसलिए कोड उस मान के आधार पर उत्पन्न होता है।

इस मामले में, हम कक्षा के सिंटैक्स से गुणों की सिंटैक्स सूची प्राप्त करने के लिए कोड बना रहे हैं और प्रत्येक नाम से default संपत्ति सेट कर रहे हैं।

उसके बाद, कक्षा के नाम के आधार पर Reset एक विधि बनाएं, पहले बनाई गई संपत्ति सूची के कोड को एम्बेड करें, और सभी गुणों के मान default को वापस Reset सेट करें विधि पूरी हो गई है।

कोड विधि में प्रत्येक वर्ग के लिए अलग से आउटपुट है contextSource.AddSource । वैसे, फ़ाइल नाम में "जी" डालने का कारण यह निर्धारित करना आसान है कि क्या यह मैन्युअल रूप से बनाया गया कोड है या बिल्ड त्रुटि होने पर स्वचालित रूप से उत्पन्न कोड त्रुटि है।

यदि आप वास्तव में इसे बनाते हैं, तो आपको "मैं फ़ील्ड को रीसेट करना चाहता हूं", "मैं इसे डिफ़ॉल्ट के अलावा अन्य प्रारंभ करना चाहता हूं", "मैं केवल स्वचालित गुणों को रीसेट करना चाहता हूं" जैसे अनुरोध प्राप्त होंगे। यदि आप उन्हें अंदर डालते हैं, तो कॉर्ड लंबा होगा, इसलिए इसे स्वयं बनाने का प्रयास करें।

आवेदन परियोजना पक्ष

इस बार, मैंने एक कोड बनाया जो कक्षा की विधि का विस्तार करता है, इसलिए मैं उचित रूप से एक कक्षा बनाऊंगा।

सामग्री निम्नानुसार हैं, लेकिन यदि गुण हैं, तो आप बाकी को उपयुक्त के रूप में उपयोग कर सकते हैं।

Player.cs

public partial class Player
{
  public string Name { get; set; } = "";


  public float PositionX { get; set; }
  public float PositionY { get; set; }

  public int Level { get; set; } = 1;

  public override string ToString()
  {
    return $"{nameof(Player)} {{{nameof(Name)} = {Name}, {nameof(PositionX)} = {PositionX}, {nameof(PositionY)} = {PositionY}, {nameof(Level)} = {Level}}}";
  }
}

Item.cs

public partial class Item
{
  public string Name { get; set; } = "";

  public float PositionX { get; set; }
  public float PositionY { get; set; }

  public int Type { get; set; }

  public override string ToString()
  {
    return $"{nameof(Item)} {{{nameof(Name)} = {Name}, {nameof(PositionX)} = {PositionX}, {nameof(PositionY)} = {PositionY}, {nameof(Type)} = {Type}}}";
  }
}

जब आप कोड बनाते हैं तो Reset विधि विस्तारित होती है, लेकिन यह Visual Studio में प्रतिबिंबित नहीं हो सकती है, इसलिए उस समय Visual Studio को पुनरारंभ करें। मुझे लगता है कि आप देख सकते हैं कि कोड स्वचालित रूप से विश्लेषक तक बढ़ाया जाता है।

इंडेंटेशन अजीब है, लेकिन आप इसे अनदेखा कर सकते हैं क्योंकि आप मूल रूप से ऑटो-जेनरेट किए गए कोड को स्पर्श नहीं करते हैं।

Program.cs यह देखने के लिए कोड लिखने का प्रयास करें कि Reset क्या आप विधि को कॉल कर सकते हैं। मुझे लगता है कि निष्पादन के परिणाम अपेक्षित रूप से परिलक्षित होते हैं।

Program.cs

Console.WriteLine("Hello, World!");

SampleClass.Hello();
Console.WriteLine();

Player player = new()
{
  Name = "Mike",
  PositionX = 10,
  PositionY = 5.5f,
  Level = 3,
};
Console.WriteLine(player);
player.Reset();
Console.WriteLine(player);
Console.WriteLine();

Item item = new()
{
  Name = "Banana",
  PositionX = 50,
  PositionY = 53.5f,
  Type = 12,
};
Console.WriteLine(item);
item.Reset();
Console.WriteLine(item);
Console.WriteLine();