کوڈ خود بخود پیدا کرنے کے لئے بصری اسٹوڈیو اور سورس جنریٹر استعمال کریں

جب صفحے کی تازہ کاری :
صفحہ تخلیق تاریخ :

آپریٹنگ ماحول

Visual Studio
  • Visual Studio 2022
.جال
  • .NET 8.0

ضروری شرائط

Visual Studio
  • یہ کچھ پرانے ورژن کے ساتھ بھی کام کرتا ہے
.جال
  • یہ کچھ پرانے ورژن کے ساتھ بھی کام کرتا ہے

سب سے پہلے

آپ کی اپنی تعریفوں کے ساتھ خود بخود کوڈ پیدا کرنے کے لئے متعدد تکنیک ہیں ، لیکن اس مضمون میں میں آپ کو سورس جنریٹر کا استعمال کرنے کا طریقہ دکھاؤں گا۔ سورس جنریٹر کا سب سے بڑا فائدہ یہ ہے کہ یہ موجودہ منصوبے کے سورس کوڈ کی ساخت کا تجزیہ کرتا ہے اور اس کی بنیاد پر نیا کوڈ تیار کرتا ہے۔ مثال کے طور پر ، جب آپ ایک نئی کلاس بناتے ہیں تو ، آپ اسے بنا سکتے ہیں تاکہ کوڈ خود بخود کلاس سے مطابقت رکھنے کے لئے شامل ہوجائے۔ آپ پروگرام کرسکتے ہیں کہ آپ کس قسم کا کوڈ پیدا کرنا چاہتے ہیں ، لہذا آپ اپنی پسند کے مطابق خودکار کوڈ جنریشن کی کسی بھی شکل کو تخلیق کرسکتے ہیں۔

کوڈ بنیادی طور پر پس منظر میں خود کار طریقے سے تیار کیا جاتا ہے اور پردے کے پیچھے منصوبے میں شامل کیا جاتا ہے۔ چونکہ یہ ایک فائل کے طور پر نظر آنے والے طریقے سے آؤٹ پٹ نہیں ہے ، لہذا اسے عام مقاصد کے لئے خود کار طریقے سے تیار کردہ کوڈ کو دوبارہ استعمال کرنے کے مقصد کے لئے استعمال نہیں کیا جاتا ہے (اگرچہ اسے فی الحال کاپی کرکے ہٹایا جاسکتا ہے)۔ تاہم ، چونکہ کوڈ منصوبے کی ساخت کے مطابق خود بخود تیار ہوتا ہے ، لہذا یہ دستی ان پٹ کی لاگت کو کم کرتا ہے اور اس کے مطابق کوڈ لکھنے کی غلطیوں کو کم کرتا ہے ، جو ایک بہت بڑا فائدہ ہے۔

اس مضمون میں ، میں وضاحت کروں گا کہ کوڈ خود بخود تیار ہونے کی جانچ کیسے کی جائے ، لہذا میں اس حد تک نہیں جاؤں گا کہ اصل میں کوڈ کا گہرائی سے تجزیہ کروں اور اعلی درجے کی آؤٹ پٹ انجام دوں۔ براہ مہربانی اسے ایک ایپلی کیشن کے طور پر دیکھیں۔

سیٹ اپ

سب سے پہلے ، بصری اسٹوڈیو انسٹال کریں۔ ایک مختصر وضاحت مندرجہ ذیل تجاویز میں خلاصہ کیا گیا ہے.

بنیادی طور پر ، آپ اسے کسی بھی منصوبے کے لئے استعمال کرسکتے ہیں ، لہذا اس سے کوئی فرق نہیں پڑتا ہے کہ آپ نے کون سا کام کا بوجھ مرتب کیا ہے۔ تاہم ، اس بار ، ایک "انفرادی جزو" کے طور پر ، ". نیٹ کمپائلر پلیٹ فارم ایس ڈی کے۔ سورس جنریٹر کی ترقی کے دوران ڈیبگنگ کے لئے یہ مفید ہے۔ اگر آپ کے پاس پہلے سے ہی بصری اسٹوڈیو انسٹال ہے تو ، آپ اسے ٹولز اور خصوصیات حاصل > کے ٹولز کے تحت بصری اسٹوڈیو مینو سے شامل کرسکتے ہیں۔

سورس جنریٹر پروجیکٹ بنانا اور تیار کرنا

سورس جنریٹر مرکزی ایپلی کیشن پروجیکٹ سے الگ ایک منصوبے میں بنایا گیا ہے۔ اس سے کوئی فرق نہیں پڑتا کہ آپ انہیں پہلے بناتے ہیں یا بعد میں اضافی بناتے ہیں۔ اس صورت میں ، میں اسے سورس جنریٹر پروجیکٹ سے تخلیق کروں گا۔

نیا پروجیکٹ بنائیں اسکرین پر ، کلاس لائبریری منتخب کریں۔

پروجیکٹ کا نام کچھ بھی ہوسکتا ہے ، لیکن ابھی CodeGenerator کے لئے ، ہم اسے اس طرح چھوڑ دیں گے۔

کلاس لائبریریوں کے لئے ، سورس جنریٹر فی الحال حمایت کرتا ہے۔ نیٹ معیار 2.0.

ایک بار جب آپ اپنا پروجیکٹ بنا لیتے ہیں تو ، نیوگیٹ کے ساتھ 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>

اگلا ، "کوڈ جنریٹر سائیڈ" پر پروجیکٹ کی خصوصیات کھولیں۔

ڈی بگ لانچ پراپرٹیز یو آئی کھولنے کے لئے لنک پر کلک کریں۔

اصل پروفائل کو حذف کریں کیونکہ آپ اسے استعمال نہیں کرنا چاہتے ہیں۔

ایک نیا پروفائل شامل کریں.

روسلین جزو منتخب کریں۔

اگر آپ نے اب تک ترتیبات بنائی ہیں تو ، آپ کو ایپلی کیشن پروجیکٹ منتخب کرنے کے قابل ہونا چاہئے ، لہذا اسے منتخب کریں۔

یہ تیاری کا اختتام ہے.

چیک کریں کہ کیا آپ ڈی بگ کرسکتے ہیں

سورس جنریٹر کوڈ کھولیں اور 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));
    });
  }
}

کوڈ کا مواد جیسا کہ تبصروں میں لکھا گیا ہے ، لہذا میں تفصیلات کو چھوڑ دوں گا۔ اسے بنائیں اور اس بات کو یقینی بنائیں کہ کوئی غلطی نہیں ہے.

جب بلڈ مکمل ہوجائے تو ، آپ کوڈ جنریٹر کے ذریعہ پیدا کردہ کوڈ کو دیکھنے کے لئے ایپلی کیشن پروجیکٹ میں "تجزیہ کاروں" کو توسیع دے سکتے ہیں۔

اگر آپ اسے نہیں دیکھتے ہیں تو ، بصری اسٹوڈیو کو دوبارہ اسٹارٹ کریں اور اسے چیک کریں۔ ایسا لگتا ہے کہ اس وقت ، بصری اسٹوڈیو کو اپ ڈیٹ نہیں کیا جاسکتا ہے جب تک کہ آپ اسے دوبارہ شروع نہ کریں۔ پروگرام بناتے وقت استعمال ہونے والے انٹیلی جنس اور نحو کو نمایاں کرنا ایک جیسے ہیں۔ تاہم ، چونکہ کوڈ خود تعمیر کے وقت ظاہر ہوتا ہے ، لہذا ایسا لگتا ہے کہ پروگرام ایپلی کیشن سائیڈ پر ظاہر ہوتا ہے۔

ایک بار کوڈ تیار ہونے کے بعد ، اسے اپنی ایپلی کیشن میں استعمال کرنے کی کوشش کریں۔ یہ ٹھیک کام کرنا چاہئے.

کوڈ کا تجزیہ اور تخلیق کریں

اگر آپ صرف کوڈ کو عام طور پر پروسیس کرتے ہیں اور اسے آؤٹ پٹ کرتے ہیں تو ، یہ دوسرے خودکار کوڈ جنریشن سے زیادہ مختلف نہیں ہے ، اور آپ سورس جنریٹر کے فوائد سے فائدہ نہیں اٹھا سکتے ہیں۔ تو اب آئیے ایپلی کیشن پروجیکٹ کے کوڈ کا تجزیہ کریں اور اس کے مطابق کوڈ تیار کریں۔

تاہم ، کوڈ کا تجزیہ کافی گہرا ہے ، لہذا میں یہاں ہر چیز کی وضاحت نہیں کرسکتا ہوں۔ فی الحال ، میں اس نقطہ تک وضاحت کروں گا جہاں آپ کوڈ کا تجزیہ اور آؤٹ پٹ کرسکتے ہیں۔ اگر آپ گہرائی میں جانا چاہتے ہیں تو، براہ مہربانی اپنی تحقیق کریں.

فی الحال ، مثال کے طور پر ، میں خود بخود کوڈ بنانا چاہتا ہوں "تمام تخلیق کردہ کلاسوں میں ایک طریقہ شامل کریں اور تمام خصوصیات کی قیمت default کو دوبارہ Reset ترتیب دیں"۔ حالیہ برسوں میں ، ایسا لگتا ہے کہ ناقابل تبدیل مثالوں کو سنبھالنے کو ترجیح دی گئی ہے ، لیکن گیم پروگراموں میں ، ہر فریم میں ایک نئی مثال بنانا ممکن نہیں ہے ، لہذا مجھے لگتا ہے کہ قیمت کو دوبارہ ترتیب دینے کے عمل کے لئے ایک استعمال ہے۔ default آپ اس کے علاوہ کچھ اور سیٹ کرنا چاہتے ہیں ، لیکن اگر آپ ایسا کرتے ہیں تو ، یہ ایک لمبی پہلی مثال ہوگی ، لہذا براہ کرم اسے خود لاگو کریں۔

کوڈ جنریٹر سائیڈ

اپنے پہلے بنائے گئے جنریٹر کو برقرار رکھنے کے مقصد کے لئے ایک نئی کلاس بنائیں۔ اگر خود بخود پیدا ہونے والا مواد تبدیل ہوجاتا ہے تو ، دوسری کلاس میں ایک نیا بنانا بہتر ہے۔

روزلین کمپائلر کوڈ کی ساخت کا خیال رکھتا ہے ، لہذا ہم یہاں جو کرنے جا رہے ہیں وہ اسٹرکچرڈ ڈیٹا کا تجزیہ کرنا اور کوڈ لکھنا ہے۔

سب سے پہلے، میں تمام کوڈ پوسٹ کروں گا. میں نے جتنا ممکن ہو کم سے کم کوڈ رکھنے کی کوشش کی ہے. اس بار یہ کام کرتا ہے ، لیکن یہ صرف ایک سطح پر ہے جو مسٹر / مس کے طور پر آگے بڑھتا ہے ، لہذا جب آپ واقعی ترقی کے ساتھ آگے بڑھتے ہیں تو ، کچھ غائب حصے ہوں گے۔ برائے مہربانی ضرورت کے مطابق اسے بہتر بنائیں۔

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 طریقہ کار میں توسیع کی جاتی ہے ، لیکن یہ بصری اسٹوڈیو میں ظاہر نہیں ہوسکتا ہے ، لہذا اس وقت بصری اسٹوڈیو کو دوبارہ اسٹارٹ کریں۔ مجھے لگتا ہے کہ آپ دیکھ سکتے ہیں کہ کوڈ خود بخود تجزیہ کار تک بڑھ جاتا ہے۔

انڈینٹیشن عجیب ہے ، لیکن آپ اسے نظر انداز کرسکتے ہیں کیونکہ آپ بنیادی طور پر خود کار طریقے سے تیار کردہ کوڈ کو نہیں چھوتے ہیں۔

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();