استفاده از ویژوال استودیو و ژنراتور منبع به طور خودکار کد تولید

صفحه به روز شده :
تاریخ ایجاد صفحه :

محیط عملیاتی

ویژوال استودیو
  • ویژوال استودیو 2022
.خالص
  • دات نت 8.0

پیش نیازها

ویژوال استودیو
  • این کار حتی با یک نسخه تا حدودی قدیمی تر کار می کند
.خالص
  • این کار حتی با یک نسخه تا حدودی قدیمی تر کار می کند

در ابتدا

تکنیک های متعددی برای تولید خودکار کد با تعاریف خود وجود دارد، اما در این مقاله من به شما نشان خواهم داد که چگونه از یک ژنراتور منبع استفاده کنید. یکی از بزرگترین مزایای یک ژنراتور منبع این است که ساختار کد منبع پروژه فعلی را تجزیه و تحلیل می کند و کد جدید را بر اساس ان تولید می کند. به عنوان مثال، هنگامی که یک کلاس جدید ایجاد می کنید، می توانید ان را به طوری که کد به طور خودکار برای مطابقت با کلاس اضافه شود. شما می توانید برنامه ریزی کنید که چه نوع کدی را می خواهید تولید کنید، بنابراین می توانید هر نوع تولید کد خودکار را که دوست دارید ایجاد کنید.

کد اساسا به صورت خودکار در پس زمینه تولید می شود و در پشت صحنه پروژه گنجانده شده است. از انجا که ان را به عنوان یک فایل به روش قابل مشاهده خروجی نیست، ان را به منظور استفاده مجدد از کد تولید خودکار برای اهداف کلی استفاده نمی شود (اگر چه می توان ان را با کپی کردن ان در حال حاضر حذف). با این حال، از انجا که کد به طور خودکار با توجه به ساختار پروژه تولید می شود، هزینه ورودی دستی را کاهش می دهد و خطاهای نوشتن کد را کاهش می دهد، که یک مزیت بزرگ است.

در این مقاله، من توضیح خواهم داد که چگونه برای بررسی اینکه کد به طور خودکار تولید می شود، بنابراین من نمی خواهم تا انجا که در واقع کد را عمیقا تجزیه و تحلیل کنم و خروجی پیشرفته را انجام دهم. لطفا ان را به عنوان یک برنامه جستجو کنید.

راه اندازی

ابتدا Visual Studio را نصب کنید. یک توضیح مختصر در نکات زیر خلاصه شده است.

اساسا، شما می توانید از ان برای هر پروژه ای استفاده کنید، بنابراین مهم نیست که چه حجم کاری را تنظیم می کنید. با این حال، این بار، به عنوان یک "جزء فردی"، ". پلت فرم کامپایلر NET SDK. این برای اشکال زدایی در طول توسعه ژنراتور منبع مفید است. اگر قبلا Visual Studio را نصب کرده اید، می توانید ان را از منوی Visual Studio در زیر Tools > Get Tools and Features اضافه کنید.

ایجاد و اماده سازی یک پروژه مولد منبع

منبع ژنراتور در یک پروژه جدا از پروژه اصلی برنامه ایجاد شده است. مهم نیست که ابتدا انها را ایجاد کنید یا بعدا موارد اضافی ایجاد کنید. در این مورد، من ان را از پروژه Source Generator ایجاد خواهم کرد.

در صفحه ایجاد پروژه جدید، کتابخانه کلاس را انتخاب کنید.

نام پروژه می تواند هر چیزی باشد، اما در حال حاضر CodeGenerator ، ما ان را به عنوان .

برای کتابخانه های کلاس، ژنراتور منبع در حال حاضر پشتیبانی می کند. NET استاندارد 2.0.

هنگامی که پروژه خود را ایجاد کردید، بسته را با Microsoft.CodeAnalysis.CSharp NuGet دریافت کنید. رفتار ممکن است بسته به نسخه متفاوت باشد، اما هیچ نقطه ای در ادامه استفاده از نسخه قدیمی وجود ندارد، بنابراین من اخرین نسخه را قرار می دهم.

سپس فایل پروژه را به عنوان کد باز کنید.

هنگامی که ان را باز می کنید، زیر را خواهید دید.

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

بعد، خواص پروژه را در "سمت ژنراتور کد" باز کنید.

برای باز کردن رابط کاربری ویژگی های راه اندازی اشکال زدایی روی پیوند کلیک کنید.

نمایه اصلی را حذف کنید زیرا نمی خواهید از ان استفاده کنید.

یک پروفایل جدید اضافه کنید.

کامپوننت 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));
    });
  }
}

محتوای کد همانطور که در نظرات نوشته شده است، بنابراین جزئیات را حذف خواهم کرد. ان را بسازید و مطمئن شوید که هیچ اشتباهی وجود ندارد.

هنگامی که ساخت کامل شد، می توانید "Analyzers" را در پروژه برنامه گسترش دهید تا کد تولید شده توسط ژنراتور کد را ببینید.

اگر ان را نمی بینید، Visual Studio را راه اندازی مجدد کنید و ان را بررسی کنید. به نظر می رسد که در این زمان، Visual Studio ممکن است به روز نشود مگر اینکه ان را دوباره راه اندازی کنید. Intellisence و syntax highlighting مورد استفاده در هنگام ایجاد برنامه ها مشابه هستند. با این حال، از انجا که کد خود را در زمان ساخت منعکس شده است، به نظر می رسد که برنامه در سمت برنامه منعکس شده است.

هنگامی که کد تولید شد، سعی کنید از ان در برنامه خود استفاده کنید. باید خوب کار کنه

تجزیه و تحلیل و تولید کد

اگر شما فقط کد را به طور معمول پردازش کنید و ان را خروجی دهید، تفاوت زیادی با سایر تولید کد خودکار ندارد و نمی توانید از مزایای ژنراتور منبع استفاده کنید. بنابراین اکنون بیایید کد پروژه برنامه را تجزیه و تحلیل کنیم و کد را مطابق با ان تولید کنیم.

با این حال، تجزیه و تحلیل کد بسیار عمیق است، بنابراین من نمی توانم همه چیز را در اینجا توضیح دهم. در حال حاضر، من تا نقطه ای که می توانید کد را تجزیه و تحلیل و خروجی کنید توضیح خواهم داد. اگر می خواهید عمیق تر بروید، لطفا تحقیقات خود را انجام دهید.

در حال حاضر، به عنوان مثال، من می خواهم به طور خودکار کد را ایجاد کنم "اضافه کردن یک روش به 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 . به هر حال، دلیل اینکه من "g" را در نام فایل قرار می دهم این است که تعیین اینکه ایا کد دستی ایجاد شده است یا خطای کد به طور خودکار تولید می شود، زمانی که خطای ساخت وجود دارد.

اگر واقعا ان را انجام دهید، درخواست هایی مانند "من می خواهم فیلد را بازنشانی کنم"، "من می خواهم ان را به غیر از پیش فرض مقداردهی اولیه کنم"، "من می خواهم فقط ویژگی های خودکار را بازنشانی کنم". اگر انها را در ان قرار دهید، طناب بلند خواهد بود، بنابراین سعی کنید ان را به تنهایی انجام دهید.

طرف پروژه برنامه

این بار، من یک کد ایجاد کردم که روش کلاس را گسترش می دهد، بنابراین من یک کلاس مناسب ایجاد خواهم کرد.

محتویات به شرح زیر است، اما اگر خواص وجود دارد، شما می توانید بقیه به عنوان مناسب استفاده کنید.

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