مسیر C#/.NET و آداپتورهای پشته · فصل ۱ — Modern C#، ASP.NET Core، EF Core و Testing · 325

Modern C#/.NET Refresh

دانش قدیمی یا پراکنده‌ی .NET دارد اما آن را با الگوهای مدرن API/data/testing/hosting همگام نکرده است. موضوع جاری: «Modern C#/.NET Refresh». می‌تواند «Modern C#/.NET Refresh» را در یک سرویس .NET محدود اجرا و با تست/diagnostics مناسب verify کند.

Modern C#/.NET Refresh — en-US

خلاصهٔ درس

دانش قدیمی یا پراکنده‌ی .NET دارد اما آن را با الگوهای مدرن API/data/testing/hosting همگام نکرده است. موضوع جاری: «Modern C#/.NET Refresh».

می‌تواند «Modern C#/.NET Refresh» را در یک سرویس .NET محدود اجرا و با تست/diagnostics مناسب verify کند.

خروجی این درس

می‌تواند «Modern C#/.NET Refresh» را در یک سرویس .NET محدود اجرا و با تست/diagnostics مناسب verify کند.

پیش‌نیازها

چرا نصب SDK تازه، پایان نوسازی نیست؟

نیما SDK جدید را نصب کرده، ویرایشگر هم آن را می‌بیند و یک قطعهٔ C# تازه اجرا می‌شود. اما سرویس سفارش هنوز net8.0 را هدف می‌گیرد و global.json ابزار قدیمی را انتخاب می‌کند. همکارش فقط چارچوب هدف را عوض می‌کند؛ این بار build می‌گذرد، ولی تنظیم Name: null مسیر نمایش نام را می‌شکند. هر دو کار واقعی انجام داده‌اند، اما هیچ‌کدام هنوز دلیل کافی برای پذیرفتن تغییر ندارند.

در این درس یک سرویس محدود را از وضعیت قبلی تا نامزد پیشنهادی دنبال می‌کنید. خروجی شما Modern .NET Baseline Matrix and Upgrade Verification Ledger v1 است: جدولی که SDK، Runtime، چارچوب هدف، زبان و وابستگی‌ها را جدا ثبت می‌کند، و دفتری که تصمیم را به تست، تشخیص خطا و نقطهٔ بازگشت متصل نگه می‌دارد. باید بتوانید توضیح دهید چه چیزی تغییر کرد، چرا لازم بود، چه چیزی هنوز معلوم نیست و کدام شاهد می‌تواند تصمیم را عوض کند.

تمرین اصلی با Python 3.10+ و فایل‌های ساختگی JSON اجرا می‌شود. انتخاب SDK و مقایسهٔ داده‌ها واقعاً در پایتون انجام می‌شود؛ رسیدهای build و تست .NET در ورودی‌ها SYNTHETIC هستند. پروژهٔ کامل و دورریختنی C# هم همراه درس است، اما اجرای آن نزد تولیدکننده UNVERIFIED مانده است. VERIFIED_SYNTHETIC فقط در محدودهٔ SYNTHETIC_ONLY معنا دارد؛ اجازهٔ انتشار سرویس یا تأیید دادهٔ واقعی نیست.

زمان کار فعال پیشنهادی ۶۰ دقیقه است. بستهٔ تمرین را باز کنید و پیش از هر تغییر، نسخهٔ ورودی را نگه دارید. این درس وارد ساخت API، مهاجرت پایگاه داده یا میزبانی سرویس نمی‌شود.

برای خواننده‌ای که C# و فراخوانی async را می‌خواند، یا از مسیر عمومی دوره وارد شده و ابتدا پل کوتاه خواندن C#/.NET همین درس را می‌گذراند، و می‌خواهد بازگشت به .NET را با شواهد انجام دهد.

این درس دورهٔ کامل زبان C# از صفر، تغییر پایگاه داده و انتشار سرویس زنده نیست؛ پل ورود فقط حداقل خواندن لازم برای همین درس و NET-002 را می‌بندد.

مسیر مناسب و شاهد ورود

پیش‌نیاز مفهومی این درس FND-003 است: حلقهٔ کنترل‌شدهٔ کار با دستیار کدنویسی. برای ورود، در پنج سطر درخواست، زمینه، نامزد، شاهد و تصمیم را از هم جدا کنید. مثال: «نام نمایش را در سرویس سفارش اصلاح کن» درخواست است؛ تنظیمات و نسخه‌ها زمینه‌اند؛ تغییر تابع نامزد است؛ خروجی آزمون شاهد است؛ پذیرفتن یا ردکردن تغییر تصمیم شماست. پاسخ مطمئن دستیار به‌تنهایی سطر شاهد را پر نمی‌کند. اگر این تفکیک دشوار است، همین مثال را با یک تغییر کوچک آشنا بازنویسی کنید و بعد ادامه دهید؛ این پل، دورهٔ مقدماتی C# نیست.

پل ترجمهٔ C#/.NET برای ورود از مسیر عمومی

اگر از مسیر عمومی دوره آمده‌اید و C# را قبلاً نخوانده‌اید، این بخش فقط آستانهٔ خواندن کدی را می‌بندد که در NET-001 و NET-002 می‌بینید؛ قرار نیست جای یک دورهٔ کامل زبان را بگیرد. اگر پنج پرسش آمادگی پایین را بدون حدس پاسخ می‌دهید، مستقیم به «چهار عدد، چهار پرسش متفاوت» بروید.

static async Task<string> LoadDisplayNameAsync(User? user)
{
    if (user is null) return "guest";
    var name = user.Name ?? "anonymous";
    await Task.Delay(1);
    return name;
}

این قطعه همان ایده‌های عمومی قبلی را با نشانه‌گذاری C# نشان می‌دهد. User? user یعنی پارامتر user از نوع User است و ممکن است null باشد. Task<string> یعنی این تابع async در پایان یک رشته تولید می‌کند. if (...) return ... همان شاخهٔ شرطی و بازگشت زودهنگام است. var name یک binding محلی است؛ var به معنی «هر نوعی در زمان اجرا» نیست و کامپایلر نوع را از عبارت سمت راست استنتاج می‌کند. عملگر ?? وقتی مقدار سمت چپ null باشد fallback سمت راست را انتخاب می‌کند. await کار async را تا تکمیل مشاهده می‌کند و سپس اجرای تابع ادامه می‌یابد. آکولادها بدنهٔ تابع را محدود می‌کنند و فراخوانی‌هایی مانند Task.Delay(1) یک عضو را روی نام نوع/فضا صدا می‌زنند.

اگر WEB-001 را گذرانده‌اید، این مقایسه کمک می‌کند: string | null در TypeScript و string? در C# هر دو شما را مجبور می‌کنند احتمال نبودن مقدار را جدی بگیرید، اما قواعد و syntax آن‌ها یکی نیست. این تشبیه فقط پل خواندن است و prerequisite تازه‌ای برای NET-001 نمی‌سازد.

سنجش ۵نقطه‌ای: در قطعهٔ بالا مشخص کنید (۱) ورودی و نوعش چیست، (۲) کجا nullable بودن مدیریت می‌شود، (۳) کدام مسیرها return دارند، (۴) binding محلی و fallback کدام‌اند، و (۵) async/await چه نتیجه‌ای را به caller برمی‌گردانند. اگر ۴ یا ۵ مورد روشن است، ادامه دهید.

اگر ۳ مورد یا کمتر روشن است، قطعه را یک بار با صدای بلند به زبان ساده بخوانید: «یک user شاید null می‌آید؛ اگر نبود guest برگردان؛ وگرنه name یا fallback را بگیر؛ یک کار async را await کن؛ name را برگردان.» سپس نام متغیر و fallback را عوض کنید و دوباره پنج پاسخ را بنویسید. معیار عبور فهم مسیر داده و کنترل است، نه حفظ syntax.

خلاصهٔ پل: برای ادامه فقط باید بتوانید پارامتر، نوع nullable، شرط، binding محلی، fallback، return و await را در یک تابع کوچک C# دنبال کنید. جزئیات عمیق‌تر زبان در محدودهٔ این درس نیست.

مسیر پایه را با مدل ذهنی و واژه‌های وضعیت شروع کنید، سپس دو مثال حل‌شده و تمرین هدایت‌شده را انجام دهید. تقسیم پیشنهادی زمان: ۸ دقیقه مرز و واژه‌ها، ۱۲ دقیقه سازوکارها، ۱۰ دقیقه مثال، ۱۲ دقیقه تمرین هدایت‌شده، ۱۲ دقیقه انتقال و ۶ دقیقه ارزیابی. این تقسیم تخمین فعالیت است، نه تضمین زمان نصب ابزار.

اگر تجربه دارید، از اختلاف نسخه‌ها در sdk_pin_old.json شروع کنید. تعریف‌هایی را که می‌توانید با مثال نقض توضیح دهید کوتاه‌تر بخوانید، اما جدول کامل، رد میان‌بر، هزینهٔ تغییر و تمرین انتقال را تحویل دهید. سرعت مسیر با حذف شاهد به دست نمی‌آید.

اگر در یک بُعد گیر کردید، مسیر ترمیم را اجرا کنید: pointer شکست را مشخص کنید، به بخش مربوط برگردید، کوچک‌ترین تغییر مرتبط را پیشنهاد دهید و فقط شواهد وابسته را دوباره بررسی کنید. همهٔ مسیرها به همان خروجی می‌رسند. ترتیب آموزشی پیشین MKN-031 و درس بعد NET-002 است؛ بستهٔ حاضر فرض نمی‌کند متن همهٔ همسایه‌ها در دسترس شماست.

شاهد ورود: برای یک تغییر کوچک، درخواست، زمینه، نامزد، شاهد مشاهده‌شده و تصمیم را جدا بنویسید.

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

چهار عدد، چهار پرسش متفاوت

SDK ابزار ساخت را انتخاب می‌کند؛ Runtime کد ساخته‌شده را اجرا می‌کند؛ TargetFramework سطح API و قرارداد هدف کامپایل را تعیین می‌کند؛ نسخهٔ C# قواعد زبانِ پذیرفته‌شده توسط کامپایلر را محدود می‌کند. این چهار مقدار به هم مرتبط‌اند، اما جانشین یکدیگر نیستند. نصب SDK 10، فایل پروژهٔ net8.0 را خودکار به net10.0 تبدیل نمی‌کند. همچنین net10.0 شمارهٔ وصلهٔ Runtime مانند 10.0.12 را پین نمی‌کند. انتخاب نسخه در .NET

دو ستون ذهنی نگه دارید: «اعلام‌شده» و «مشاهده‌شده». global.json یک درخواست انتخاب ابزار است؛ خروجی انتخاب ابزار در زمینهٔ مشخص، مشاهدهٔ جداگانه می‌خواهد. مقدار Runtime در فایل تمرین نیز ادعای همان داده است، نه مدرک نصب روی رایانهٔ شما. در انتشار وابسته به چارچوب، انتخاب وصلهٔ Runtime می‌تواند طبق قواعد میزبان جلو برود؛ انتشار self-contained Runtime خود را همراه دارد و رسیدگی به آن جداست. از موفقیت یکی، وضعیت دیگری را نتیجه نگیرید.

معادل متنی نمودار: وضعیت قبلی و زمینه را ثبت کن؛ SDK، Runtime، TFM و زبان را تفکیک کن؛ پشتیبانی و وابستگی‌ها را بررسی کن؛ تغییر ناسازگارِ مرتبط را به رفتار و تست وصل کن؛ نامزد محدود بساز؛ build، تست و diagnostics هم‌زمینه را بخوان؛ نبودن یا کهنه‌بودن شاهد به HOLD می‌رسد، تناقض به ترمیم و اجرای دوباره، و کامل‌بودن ورودی ساختگی فقط به VERIFIED_SYNTHETIC؛ سپس دفتر نسخه‌دار را نگه دار.

NET-001-F1 — بازیابی مرز: چرا «SDK 10 نصب است» برای پذیرفتن سرویس کافی نیست؟ چهار پرسش بالا را با یک شاهد مناسب برای هرکدام پاسخ دهید و یک کار خارج از محدوده نام ببرید. اگر فقط چهار شماره نوشته‌اید، هنوز تفاوت اعلام و مشاهده را نشان نداده‌اید.

از پین نسخه تا معنی وضعیت

در سناریوی سفارش، وضعیت قبلی 8.0.100 / 8.0.0 / net8.0 / 12.0 است؛ این چهار مقدار تاریخی‌اند و پیشنهاد نصب امروز نیستند. نامزد تمرین 10.0.401 / 10.0.12 / net10.0 / 14.0 است. در تاریخ بررسی 2026-09-12، صفحهٔ انتشار، .NET 10 را LTS با پایان پشتیبانی 2028-11-14 و .NET 8 را با پایان 2026-11-10 ثبت می‌کند؛ وصله‌های ثبت‌شده به‌ترتیب 10.0.12 و 8.0.31 هستند. عنوان LTS به‌تنهایی نشان نمی‌دهد یک وصلهٔ قدیمی شرایط سرویس‌دهی جاری را دارد. سیاست پشتیبانی

پین ابزار نامزد در پوشهٔ dotnet چنین است:

{"sdk":{"version":"10.0.401","rollForward":"disable","allowPrerelease":false}}

disable یعنی همان نسخه باید موجود باشد. اگر فقط 10.0.112 نصب باشد، این درخواست به 10.0.401 تبدیل نمی‌شود. مدل پایتون فقط disable و latestPatch را پیاده می‌کند؛ دومی بیشترین نسخهٔ نصب‌شدهٔ مجاز در همان feature band و نه پایین‌تر از درخواست را برمی‌گزیند. برای نمونه، 10.0.100 با latestPatch می‌تواند 10.0.112 را انتخاب کند، اما 10.0.401 عضو همان باند نیست. سیاست‌های دیگر در مدل UNSUPPORTED می‌مانند. global.json

در CLI واقعی، مسیر شروع جست‌وجوی global.json اهمیت دارد؛ انتخاب ابزار در MSBuild نیز می‌تواند از زمینهٔ solution یا project آغاز شود. تمرین یک شیء JSON صریحِ ازقبل‌انتخاب‌شده می‌گیرد و پیمایش پوشه، IDE یا همهٔ MSBuild را شبیه‌سازی نمی‌کند. همین محدودیت را کنار نتیجه بنویسید.

واژه‌های تصمیم را دقیق استفاده کنید: UNKNOWN یعنی داده قابل تعیین نیست؛ UNSUPPORTED یعنی ترکیب یا سیاست از قرارداد پشتیبانی بیرون است؛ UNVERIFIED یعنی شاهد کافی نداریم؛ STALE یعنی شاهد به زمان یا نامزد فعلی نمی‌خورد؛ REVIEW_REQUIRED یعنی بررسی لازم باقی مانده؛ FAILED یعنی نتیجه با انتظار تناقض دارد. HOLD جمع‌بندی وجود مانع است. این علت‌ها با میانگین نمره یا یک PASS سبز حذف نمی‌شوند.

تغییر را به رفتار وصل کنید

در فایل پروژهٔ نمونه، TargetFramework برابر net10.0، LangVersion برابر 14.0 و Nullable برابر enable است. مقدار صریح زبان یک انتخاب بازتولیدپذیر برای این تمرین است؛ در پروژهٔ واقعی، پیش‌فرض زبان با TFM هماهنگ می‌شود و پین صریح نسخهٔ پایین‌تر نیز ممکن است. latest را به‌جای تصمیم نسخه‌دار نگذارید. استفاده از زبان جدیدتر از نسخهٔ پیش‌فرضِ پشتیبانی‌شدهٔ TFM، مسیر پشتیبانی‌شده‌ای نیست. تنظیم نسخهٔ زبان

در تاریخ بررسی، C# 14 نسخهٔ پایدار مربوط به .NET 10 است. انتخاب آن به معنی نیاز به بازنویسی همهٔ کد نیست. برای نمونه، تغییر قواعد انتخاب overload مرتبط با Span ممکن است بعضی فراخوانی‌ها را تغییر دهد. در فهرست ساختگی منبع این تمرین، تعداد چنین فراخوانی‌هایی صفر است؛ بنابراین این مورد با دلیل مشخص NOT_APPLICABLE است. نبودن نمونه در این فهرست، ممیزی خودکار یک مخزن واقعی محسوب نمی‌شود. C# 14، تغییر overload

مورد مرتبط ما تنظیم JSON است. پیش از .NET 10، ارائه‌دهندهٔ JSON مقدار null را به رشتهٔ خالی تبدیل می‌کرد و binder نیز با null رفتار پیشین خود را داشت؛ در .NET 10، null حفظ می‌شود و binder می‌تواند آن را تخصیص دهد. پس نگویید JSON null قدیمی همیشه مقدار اولیهٔ property را دست‌نخورده نگه می‌داشت. برای property رشته‌ای مثال، کلید غایب و null صریح یکی نیستند. تغییر حفظ null

قرارداد محلی نمایش نام می‌گوید مقدار غایب، null و رشتهٔ خالی همگی «Guest» بدهند و " Neda " به "Neda" تبدیل شود. کدی که ابتدا name.Trim() را اجرا می‌کرد با null واقعی ممکن است شکست بخورد. ترمیم کوچک چنین است:

static string DisplayName(string? name) =>
    string.IsNullOrWhiteSpace(name) ? "Guest" : name.Trim();

این guard تصمیم محصول دربارهٔ مقدار خالی را اجرا می‌کند. nullable analysis هشدار کامپایل می‌دهد؛ guard زمان اجرا تولید نمی‌کند. TreatWarningsAsErrors=true در پروژهٔ نمونه قرارداد سخت‌گیرانهٔ محلی است، نه ادعای اینکه همهٔ تیم‌ها باید بدون بررسی یکسان تنظیم شوند. مرز کتابخانهٔ خارجی و annotationهای نامعلوم را همچنان باید بررسی کرد. nullable references

مرز async نیز صرفاً اضافه‌کردن async نیست. متد سرویس در این تمرین Task برمی‌گرداند؛ فراخواننده آن را await می‌کند، خطای وابستگی را در همان مسیر می‌بیند و cancellation را پیش از اثر بررسی می‌کند. async void برای این قرارداد سرویس مناسب نیست؛ کاربرد معمول آن handler رویداد است. نگه‌داشتن lifetime وابستگی scoped تا پایان await لازم است؛ برگرداندن کار ناتمام از scope بسته‌شده می‌تواند شیء dispose‌شده را مصرف کند. پروژهٔ همراه هم خطا و لغو را بررسی می‌کند و هم dispose پس از پایان کار را. این آزمون کوچک، رفتار زمان‌بندی همهٔ برنامه‌های async را ثابت نمی‌کند. بازگشت async، طول عمر DI

NET-001-F2 — تشخیص منفی: در regression_failed.json چرا build موفق با شکست config_null جمع می‌شود؟ pointer نتیجهٔ آزمون را پیدا کنید، تفاوت nullable warning و guard را توضیح دهید و ترمیمی پیشنهاد کنید که انتظار عمومی تست را برای سبزکردن نتیجه تغییر ندهد.

مثال حل‌شده: نامزد منسجم و SDK اشتباه

فایل baseline.json یک ثبت ساختگی از سرویس orders-refresh است. اول شیء before را بخوانید، بعد candidate را؛ نام fixture نشانهٔ وضعیت قبلی نیست. این فایل هر دو وضعیت و رسیدهای نامزد را در یک زمینه دارد. برنامهٔ زیر انتخاب ابزار و اختلاف فیلدها را واقعاً محاسبه می‌کند:

python inspect_baseline.py fixtures/baseline.json
python inspect_project.py

انتخاب مدل برای نامزد 10.0.401 است. اختلاف‌ها شامل چهار نسخه، global.json، نسخهٔ وابستگی ساختگی، سیاست null و بازنگری منبع‌اند. مقدار nullable، قرارداد Task و مرز lifetime تغییر نکرده‌اند؛ باقی‌ماندنشان به معنی بی‌نیازی از تست نیست. جدول خلاصهٔ زیر را کنار جدول کامل resources/tables/NET-001-T1.csv بخوانید؛ جدول کامل ۲۳ ستون و دو ردیف دارد و نگاشت همهٔ فیلدها در field_map.json آشکار است.

بُعدوضعیت قبلینامزد سفارشدلیل بررسی
SDK / Runtime8.0.100 / 8.0.010.0.401 / 10.0.12ابزار و اجرای هدف جدا
TFM / C#net8.0 / 12.0net10.0 / 14.0سطح API و زبان جدا
global.jsonپین دقیق 8.0.100پین دقیق 10.0.401نصب ابزار کافی نیست
بستهٔ ساختگیDisplayAdapter 1.0DisplayAdapter 2.0ماتریس تأیید محدودهٔ هدف
رفتار nulltrim سپس fallbackguard سپس trimCFG_NULL_10 مرتبط است
build / تست / تشخیصدوباره ارزیابی نشدهPASS ساختگی / ۷ مورد / PASS ساختگیرسیدها هم‌زمینه‌اند
بازبینی / بازگشتتأیید انسانی ارائه نشدهبازبینی و بازگشت ساختگیهیچ ادعای سرویس واقعی

در contracts.json، نسخهٔ 1.0 بستهٔ خیالی فقط برای net8.0 در ماتریس تمرین تأیید شده و نسخهٔ 2.0 برای هر دو هدف. این فایل حقیقت NuGet نیست. در پروژهٔ واقعی، پایین‌تر بودن TFM یک بسته به‌تنهایی ناسازگاری را ثابت نمی‌کند؛ انتخاب دارایی سازگار، restore و رفتار برنامه باید بررسی شوند. در این تمرین فقط مجموعهٔ صریحِ تأییدشده ملاک است.

اکنون هفت مشاهدهٔ تست را با انتظار عمومی مقایسه کنید، نه فقط واژهٔ PASS را. نبودن و null و خالی‌بودن نام، نام دارای فاصله، خطای await، لغو پیش از اثر و طول عمر scope پوشش دارند. build، تست‌ها، diagnostics و review باید به service، context و چهار digest مربوط متصل باشند. بازگشت نیز همان digest قبلی را گزارش می‌کند. برای این ورودی و تاریخ نمونه، هیچ مانع قرارداد باقی نمی‌ماند: VERIFIED_SYNTHETIC با دامنهٔ SYNTHETIC_ONLY. این حکم صحت اجرای واقعی هفت آزمون C# را اعلام نمی‌کند.

در sdk_pin_old.json هر دو SDK نصب‌شده اعلام شده‌اند، اما global.json نامزد هنوز 8.0.100 را با disable می‌خواهد. مدل همان 8 را انتخاب می‌کند، درحالی‌که نامزد SDK 10 و net10.0 ادعا دارد. سه علت را نگه دارید: SDK_SELECTION_MISMATCH/FAILED، BASELINE_CONTRACT_FAILURE/FAILED و CONTEXT_STALE/STALE. تغییر پین digest نامزد را عوض کرده و رسیدهای قبلی دیگر به آن وصل نیستند. تصمیم HOLD است. ترمیم پیشنهادی، هماهنگ‌کردن پین و سپس تولید دوبارهٔ رسیدهای وابسته در زمینهٔ جدید است؛ ویرایش دستی هش رسید قدیمی جای اجرای تازه را نمی‌گیرد.

شکست را کوچک کنید، علت‌ها را حذف نکنید

فایل‌های منفی هرکدام یک میان‌بر را آشکار می‌کنند. tfm_without_review فهرست بررسی تغییر ناسازگار را خالی می‌کند؛ رفع آن پیدا‌کردن دلیل ارتباط تغییر با منبع و تست است، نه نوشتن «همه بررسی شد». language_package_conflict نسخهٔ وابستگی را به ماتریس تأییدنشده برمی‌گرداند؛ اگر هم‌زمان اتصال رسید کهنه شود، هر دو علت باقی‌اند. nullable_disabled قرارداد هشدار را تضعیف می‌کند و async_void قرارداد مشاهدهٔ پایان کار را می‌شکند. سبزبودن رسیدهای کپی‌شده این تفاوت‌ها را خنثی نمی‌کند.

در missing_diagnostic، ارجاع capture خالی است؛ حتی اگر فیلدهای خروجی معقول باشند، شاهد تشخیصی UNVERIFIED می‌ماند. در stale_review، هش نامزد در بازبینی با نامزد فعلی فرق دارد؛ نام بازبین و واژهٔ REVIEWED برای پوشاندن STALE کافی نیستند. سازگاری شش اتصال service، context، baseline، candidate، source و tools را جدا بررسی کنید.

سه اشتباه رایج در ساخت دفتر وجود دارد. نخست، عوض‌کردن برچسب FAILED بدون تغییر مشاهده؛ تستی که خطا داده با ویرایش برچسب درست نمی‌شود. دوم، برابرگرفتن نبودن شاهد با شاهد منفی؛ capture گمشده یعنی نمی‌دانیم، نه اینکه رفتار مورد انتظار دیده شده است. سوم، پاک‌کردن علت‌های مستقل پس از یافتن اولین شکست. اگر diagnostics دادهٔ خصوصی ساختگی نشان دهد و یک تست هم شکست خورده باشد، مشکل حریم داده و شکست تست هر دو باید باقی بمانند.

در این تمرین فقط دادهٔ ساختگی به کار ببرید. پیام خطا، توکن، connection string یا دادهٔ مشتری را برای «کامل‌ترشدن» دفتر اضافه نکنید. نام testcase، نسخه، context و نتیجهٔ پاک‌سازی‌شده برای توضیح لازم کافی‌اند. ترمیم کوچک از فیلد علت آغاز می‌شود، ولی هر شاهدی که به digest تغییرکرده وابسته است باید تازه شود. کوچک‌بودن تغییر کد، مجوز حفظ رسید نامرتبط نیست.

مسیر باتجربه: از تغییر کمتر دفاع کنید

فرض کنید همکار شما می‌گوید برای یکدست‌شدن همهٔ سرویس‌ها، SDK باند 400 و C# 14 را اجباری کنیم. یکدستی می‌تواند هزینهٔ پشتیبانی را کم کند، اما شاهدی دربارهٔ نیاز همین سرویس نیست. در صفحهٔ دانلودِ بررسی‌شده، SDKهای 10.0.401 و 10.0.112 هر دو همراه Runtime 10.0.12 و C# 14 فهرست شده‌اند. قدیمی‌تر بودن شمارهٔ feature band به‌تنهایی به معنی وصلهٔ منقضی نیست؛ این صفحه نیز تأیید سازگاری همهٔ IDEها و سیستم‌عامل‌های شما نیست. دانلود .NET 10

برای فشرده‌کردن مسیر، سه دفاع کوتاه بنویسید: چه نیاز منبعی تغییر زبان را لازم می‌کند؟ کدام قاعدهٔ انتخاب ابزار در محیط تیم واقعاً اعمال می‌شود؟ چه شاهدی نشان می‌دهد بسته‌ها و رفتار باقی‌مانده روی نامزد کار می‌کنند؟ اگر پاسخ اول «هیچ نیاز فعلی» است، آن را صریح بنویسید؛ این پاسخ دو پرسش دیگر را حذف نمی‌کند.

یک مثال نقض هم لازم است: سرویسی با TFM یکسان ولی pin زبان پایین‌تر می‌تواند همان کد را بدون استفاده از ویژگی جدید کامپایل کند. در مقابل، یک نیاز واقعی به ویژگی جدید ممکن است تغییر زبان و تست‌های مرتبط را توجیه کند. انتخاب بین این دو، از نام «مدرن» به دست نمی‌آید.

برای ترمیم تصمیم خود، به نزدیک‌ترین شاهد برگردید: انتخاب ابزار غلط به global.json و نتیجهٔ resolver، خطای نام به قرارداد null و testcase، و کار گمشدهٔ async به Task، cancellation و lifetime مربوط است. اگر تنها راه دفاع شما نشان‌دادن شمارۀ نسخهٔ بزرگ‌تر است، به مدل ذهنی برگردید و اعلام و مشاهده را جدا کنید.

ارتقا، تثبیت یا توقف موقت؟

در یک پروژهٔ واقعی، ماندن کوتاه‌مدت روی خانوادهٔ پشتیبانی‌شده با وصلهٔ جاری ممکن است فرصتی برای جمع‌آوری شواهد بخرد. هزینه‌اش نزدیک‌شدن به پایان پشتیبانی و تکرار کار بعدی است. ارتقا به .NET 10 افق پشتیبانی طولانی‌تری می‌دهد، ولی سازگاری منبع، ابزار و وابستگی را خودکار حل نمی‌کند. نوسازی syntax بدون نیاز رفتاری نیز هزینهٔ مرور کد و diff را زیاد می‌کند؛ فایده‌اش باید روشن باشد.

برای سرویس سفارش، هدف net10.0 جزو قرارداد تمرین است؛ بنابراین «فعلاً net8 می‌مانم» پاسخ موفق همین قرارداد نیست. می‌توانید آن را به‌عنوان پیشنهاد تغییر دامنه با HOLD توضیح دهید. برای یک تصمیم خارج از تمرین، مالک سرویس باید هدف، زمان، هزینه و ریسک را تعیین کند. مستند Microsoft دربارهٔ سازوکار پلتفرم است؛ اجازهٔ تغییر تولید یا سیاست تیم را صادر نمی‌کند.

فهرست تغییرهای ناسازگار یک نقشهٔ جست‌وجوست. دو مورد این درس برای موجودی کوچک ساختگی کافی تعریف شده‌اند، اما ممیزی جامع هر مهاجرت 8 به 10 نیستند. در سرویس واقعی، نسخه‌های میانی، کتابخانه‌های مصرف‌شده، API و ابزارهای مؤثر را هم بررسی کنید؛ «دو لینک خواندم» معادل «هیچ تغییر مرتبط دیگری وجود ندارد» نیست. فهرست .NET 10

نقطهٔ توقف را پیشاپیش بنویسید: بستهٔ بدون شاهد سازگاری، تست متناقض، diagnostics بی‌ارجاع یا نبودن نقطهٔ بازگشت مانع پذیرش است. rollback این درس فقط بازگرداندن snapshot فایل‌های دورریختنی را مدل می‌کند؛ تراکنش پایگاه داده، اثر خارجی و بازگشت استقرار را تضمین نمی‌کند. از کنار هم گذاشتن امتیاز هزینه و امتیاز تست، میانگینی نسازید که شکست قطعی را بپوشاند.

تمرین هدایت‌شده و انتقال مستقل

در پوشهٔ learner، فهرست ورودی‌ها را بخوانید و برای regression_failed یک پاسخ تازه بسازید:

python run_checks.py --inventory
python run_checks.py --template fixtures/regression_failed.json submission.guided.json --as-of 2026-09-12

پیش از بررسی پاسخ، خودتان readiness_state، همهٔ issues، evidence_refs، rationale، minimal_repair و review_trigger را پر کنید. در فایل هدایت‌شده، از testcase با شناسهٔ config_null شروع کنید؛ index را از آرایهٔ همین فایل بخوانید و با pointer مانند /tests/1/observed به مشاهده ارجاع دهید. راهنمایی این است: PASS مربوط به build چه چیزی را می‌گوید، و مشاهدهٔ این testcase کدام ادعای رفتاری را رد می‌کند؟ سپس تفاوت ترمیم کد با دست‌کاری انتظار را توضیح دهید.

python run_checks.py --check fixtures/regression_failed.json submission.guided.json

پیام STRUCTURE_PASS_SEMANTICS_UNREVIEWED یعنی شکل، اتصال و ارجاع‌های پاسخ پذیرفته شده‌اند؛ درستی استدلال یا کامل‌بودن علت‌ها هنوز بررسی معنایی می‌خواهد. بررسی‌کننده عمداً پاسخ نهایی انتقال را تولید نمی‌کند. برای تکرار، نام خروجی تازه انتخاب کنید؛ ابزار فایل موجود را بازنویسی نمی‌کند.

اکنون transfer_existing.json را مستقل بررسی کنید. سرویس دوم از قبل net10.0 است، SDK باند قدیمی‌تر دارد و زبان پیشین را صریح پین کرده است. تعریف‌ها و انتظارهای قابل‌مشاهده در contracts.json هستند، اما حکم جمع‌بندی و متن پاسخ آماده نیست. وضعیت قبلی و نامزد سرویس دوم را از خودش استخراج کنید؛ انتخاب واقعی مدل را دوباره به دست آورید؛ موجودی منبع، سازگاری و receiptها را با هویت همین سرویس بسنجید. مشخص کنید چه چیزی، اگر چیزی لازم است، باید عوض شود؛ هر توصیه را به دلیل و هزینه وصل کنید.

NET-001-F3 — انتقال: بدون نگاه به برچسب تصمیم مثال حل‌شده، پاسخ مستقل بسازید. در submission.transfer.json همهٔ علت‌ها، شواهد و برنامهٔ ترمیم را بنویسید. ورودی اصلی را حفظ کنید؛ اگر نسخهٔ پیشنهادی می‌سازید، نام جدا بدهید و رسید تازهٔ ساختگی را همان‌طور علامت بزنید. تغییر service یا هش روی رسید قدیمی، مشاهدهٔ تازه نیست. تصمیم دربارهٔ نصب نسخه و تصمیم دربارهٔ کفایت شاهد را جدا توضیح دهید.

چه چیزی واقعاً اجرا شده و چه چیزی را باید تحویل داد؟

انتخاب SDK و محاسبهٔ digest در inspect_baseline.py اجرای واقعی پایتون است. inspect_project.py فقط چهار اعلان صریح XML را می‌خواند؛ imports، Directory.Build.props، شرط‌ها و اجرای MSBuild را ارزیابی نمی‌کند. مدل resolver نیز پیاده‌سازی کامل میزبان .NET نیست. receiptهای JSON دربارهٔ build، تست و diagnostics دادهٔ ساختگی برای تمرین تصمیم‌اند. این مرز را در خروجی‌تان حفظ کنید، حتی اگر همهٔ کنترل‌های پایتون سبز شوند.

پروژهٔ dotnet/RefreshProbe.csproj همراه Program.cs، global.json و NuGet.Config کامل ارائه شده است. این console probe از framework reference استفاده می‌کند و بستهٔ خیالی DisplayAdapter را نصب نمی‌کند؛ بنابراین نمونهٔ رفتار guard/async/scope است، نه ساخت واقعی همان سرویس خیالی با آن بسته. روی محیط جداگانه‌ای با SDK دقیق و targeting packهای لازم، از پوشهٔ dotnet می‌توان چنین اجرا کرد:

dotnet --version
dotnet restore --configfile NuGet.Config
dotnet build --no-restore
dotnet run --no-build --no-restore

آخرین فرمان assertionهای برنامهٔ کنسولی را اجرا می‌کند؛ این پروژه xUnit نیست و dotnet test جانشین آن محسوب نمی‌شود. در این محیط تولید، راه‌اندازی CoreCLR شکست خورد و build انجام نشد. خروجی موفق این فرمان‌ها را برای این بسته ادعا نمی‌کنیم. اگر شما اجرا کردید، فرمان، نسخه‌های مشاهده‌شده، exit code، context، هش دقیق منبع و خروجی پاک‌سازی‌شده را در دفتر جدا نگه دارید؛ scope آن DISPOSABLE_CONSOLE_ONLY است. نتیجهٔ واقعی را در یک receipt ساختگی جا نزنید و بدون بازتعریف قرارداد، علامت دامنه را عوض نکنید.

برای تحویل تمرین اصلی، جدول کامل دو وضعیت، دفتر شواهد، پاسخ JSON و توضیح علت‌ها لازم‌اند. baseline_digest و candidate_digest از JSON مرتب‌شده محاسبه می‌شوند؛ source_digest فقط موجودی ساختگیِ اعلام‌شده و tools_sha256 فایل مدل پایتون را متصل می‌کند. این هش‌ها امضای مستقل اجرا یا هش همهٔ فایل‌های C# نیستند. بازبین ساختگی fixture هم شخصی نیست که سرویس واقعی شما را تأیید کرده باشد؛ مالک انسانی در این بسته معرفی نشده است.

ارزیابی در تاریخ نمونه را با ادعای امروز مخلوط نکنید. برای بازسازی مثال از 2026-09-12 استفاده کنید؛ برای ادعای جاری تاریخ واقعی را ثبت کنید. پس از 2026-10-18 یا تغییر مهم نسخه، سیاست و زمینه، REVIEW_REQUIRED را حفظ کنید تا منبع و شواهد تازه شوند. پاسخ درست باید قابل بازسازی از ورودی‌های آشکار باشد؛ توضیحی که فقط بر «دستیار گفت» تکیه دارد کافی نیست.

ارزیابی: از جدول تا تصمیم قابل دفاع

NET-001-S1 — مرز: در یک بند توضیح دهید نوسازی این درس چه چیزی را ثابت می‌کند و چه چیزی را ثابت نمی‌کند. SDK، Runtime، TFM و زبان را جدا کنید؛ یک وضعیت غیرموفق مناسب را با علت به کار ببرید و نشان دهید چرا این کار جای درس API یا تست و میزبانی را نمی‌گیرد.

NET-001-S2 — کاربرد: برای baseline.json، جدول کامل و دفتر شواهد را بسازید. هر ۱۷ بُعد حیاتی باید حضور داشته باشد: sdk_version، runtime_version، target_framework، csharp_language_version، support_channel، support_end، global_json_ref، package_target_compatibility، nullable_context، breaking_change_ref، compile_state، test_state، diagnostic_state، baseline_digest، candidate_digest، review_state و audit_event_id. فیلدهای async، configuration، DI و review_owner در جدول کامل نیز باقی بمانند. به هر ادعا ارجاع بدهید و دامنهٔ تصمیم را بنویسید.

NET-001-S3 — تشخیص: بدون حذف SDK جدید از فهرست نصب، sdk_pin_old.json را توضیح دهید. نسخهٔ انتخاب‌شده را به دست آورید، فیلد نامعتبر و پیامدش را مشخص کنید و ترمیم حداقلی بنویسید. بگویید پس از اصلاح پین، کدام رسیدها باید به علت تغییر context یا digest دوباره تولید شوند. نقل قول «روی ماشین من کار می‌کند» شاهد کافی نیست.

NET-001-S4 — انتقال: برای سرویس موجود روی net10.0، جدول و پاسخ مستقل بسازید. pin زبان و باند SDK را با نیاز منبع و snapshot پشتیبانی مقایسه کنید؛ تغییر لازم و اختیاری را جدا کنید؛ اعتبار تک‌تک رسیدها را از نو بسنجید. دلیل شما باید از سرویس دوم بیاید، نه از نسخه‌ها یا حکم سرویس اول. برنامهٔ ترمیم، نقطهٔ توقف و محرک بازبینی بعدی را بنویسید.

پاسخ کافی در پنج بُعد هم‌زمان قابل قبول است: مرز درست، اتصال دقیق، حفظ شواهد منفی، تصمیم محدود و انتقال مستقل. کد صحیح با دلیل نادرست یا pointer بی‌ارتباط کافی نیست. اگر یک بُعد ناکافی است، همان را با fixture مربوط ترمیم کنید؛ میانگین بالا آن را حذف نمی‌کند. پرسش گفت‌وگوی پایانی: کدام میان‌بر در تیم شما بیشترین اعتماد کاذب می‌سازد و کوچک‌ترین شاهدی که آن را آشکار می‌کند چیست؟ فقط نمونهٔ ساختگی یا پاک‌سازی‌شده بیاورید.

منابع، تاریخ و مرز ادعا

منابع رسمی این نسخه در 2026-09-12 بررسی شده‌اند و موعد بازبینی 2026-10-18 است. انتشارها و پشتیبانی .NET، نمای کلی .NET 10، C# 14 و فهرست تغییرهای ناسازگار چهار مرجع اصلی‌اند. ده مرجع تکمیلی مربوط به سازوکارهای توضیح‌داده‌شده در متن و resources/references/sources.json ثبت شده‌اند. شماره‌های وصله و عبارت «پایدار جاری» همگی به همین تاریخ مقیدند.

سه نوع جمله را جدا بخوانید. واقعیت منبع: سازوکار global.json، رفتار null و تاریخ پشتیبانی را مرجع پلتفرم بیان می‌کند. استنباط این مورد: از موجودی ساختگی و انتظار عمومی نتیجه می‌گیریم کدام تغییر به همین سناریو مربوط است. طراحی تمرین: فیلدهای دفتر، سخت‌گیری هشدارها، انتظار Guest، واژه‌های تصمیم و شرط هم‌زمانی شاهدها انتخاب آموزشی‌اند. وجود لینک رسمی، این طراحی‌ها را به فرمان Microsoft تبدیل نمی‌کند.

با تغییر نسخهٔ هدف، بسته، رفتار تنظیمات، منبع مدل، محیط یا زمان، شواهد وابسته را بازبینی کنید. snapshot قبلی را بی‌صدا بازنویسی نکنید؛ نسخهٔ تازه و دلیل تفاوت را نگه دارید. این درس با متن و تمرین دو زبان ارائه شده، اما تجربهٔ مالک یا تأیید انسانی ساخته نشده است. پس از تکمیل ارزیابی، مسیر آموزشی به NET-002 می‌رود؛ نتیجهٔ این درس همان دفتر محدود نوسازی است.

ارزیابی

پاسخ‌نامه‌ی این درس در بسته‌ی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار می‌فرستد.

ملاک‌های ارزیابی (rubric)

هر پنج بُعد لازم‌اند؛ میانگین، شکست را حذف نمی‌کند. All five dimensions must be sufficient; averaging cannot erase a failure.

بُعد / Dimensionکافی / Sufficientناکافی / Insufficient
مرز / BoundarySDK، Runtime، TFM، زبان و دامنه جدا / Separates four version facts and execution scopeنصب را تأیید نوسازی می‌داند / Treats installation as verification
اتصال / Bindingورودی، سیاست، قرارداد، زمینه و هش‌ها دقیق / Exact input, policy, contracts, context and hashesرسید کپی‌شده با هش ویرایش‌شده / Relabels copied receipts
منفی / Negativeهمهٔ علت‌های نوع‌دار و مشاهدهٔ تست / All typed causes and observed test outcomesPASS را جای مشاهده می‌گذارد / Substitutes PASS for observations
تصمیم / Decisionحکم محدود، ترمیم و محرک بازبینی / Bounded state, minimal repair and review triggerادعای build یا تولید بی‌شاهد / Unsupported build or production claim
انتقال / Transferاز نیاز سرویس دوم دوباره نتیجه می‌گیرد / Recomputes from the second service's needsطرح ارتقای اول را کپی می‌کند / Copies the first migration plan

کد درست بدون استدلال کافی نیست. Correct issue codes without causal reasoning are insufficient. A Python resolver pass does not establish C# compilation or live runtime behavior. Human service-owner approval is not supplied.

منابع و تازگی

· TOOL_VOLATILE
.NET releases and support lifecycle, including currently supported versions and end-of-support dates.
· TOOL_VOLATILE
Microsoft documentation for C# 14 language features and .NET 10 support.
· TOOL_VOLATILE
Microsoft catalog of .NET 10 breaking changes by technology area.
· TOOL_VOLATILE
Microsoft .NET 10 overview and LTS release boundary.
·
SDK resolution, stable versions and roll-forward policies
·
TFM-related language defaults and explicit version selection
·
Date-bound support lifecycle and current servicing patches
·
JSON provider and configuration binder null change in .NET 10
·
Compile-time null analysis, not runtime guards
·
Task, awaiting and async void boundaries
·
Distinct SDK, target framework and runtime selection
·
Dependency lifetimes and disposal ownership
·
SDK 10.0.401 and 10.0.112 with runtime 10.0.12 in checked listing
·
C# 14 Span-related overload resolution changes

مطالعهٔ موارد (case studies)

فایل‌های آزمایشگاه

نیاز: Python 3.10+، فقط کتابخانهٔ استاندارد. از همین پوشه اجرا کنید:

python inspect_baseline.py fixtures/baseline.json
python inspect_project.py
python run_checks.py --inventory
python run_checks.py --template fixtures/regression_failed.json submission.json --as-of 2026-09-12
python run_checks.py --check fixtures/regression_failed.json submission.json

میان دو فرمان آخر، تصمیم، همهٔ علت‌های نوع‌دار، pointer، استدلال، ترمیم و محرک بازبینی را تکمیل کنید. STRUCTURE_PASS_SEMANTICS_UNREVIEWED فقط شکل و اتصال را می‌سنجد. خروجی موجود بازنویسی نمی‌شود.

baseline و sdk_pin_old حل‌شده‌اند؛ regression_failed هدایت‌شده و transfer_existing مستقل است. policy.json همهٔ قواعد، contracts.json انتظارها و منابع snapshot و resources جدول کامل، نگاشت، منابع و ارزیابی را می‌دهند. تصمیم انتقال را خودتان بسازید. نامزد و رسید تازهٔ پیشنهادی را در فایل جدا با برچسب ساختگی نگه دارید.

پایتون فقط resolver محدود و مقایسه را واقعاً اجرا می‌کند. ورودی global.json ازقبل انتخاب شده است؛ فقط disable و latestPatch برای نسخهٔ پایدار پشتیبانی می‌شوند. inspect_project چهار اعلان صریح را می‌خواند و MSBuild را اجرا نمی‌کند. داده‌های build، تست و diagnostics مصنوعی‌اند، نه اجرای C#.

پوشهٔ dotnet پروژهٔ کنسولی کامل با assertion دارد. از پوشهٔ آن و با SDK دقیق 10.0.401 و targeting pack لازم، dotnet restore --configfile NuGet.Config، سپس dotnet build --no-restore و dotnet run --no-build --no-restore اجرا می‌شوند. dotnet test جانشین harness کنسولی نیست. پروژه از framework reference استفاده می‌کند؛ بستهٔ خیالی تمرین را نصب نمی‌کند. این اجرا در محیط تولیدکننده UNVERIFIED است: CoreCLR راه نیفتاد و build اجرا نشد. نتیجهٔ واقعی خودتان را با هش منبع C# و context در دفتر جدا ثبت کنید؛ آن را در receipt ساختگی جا نزنید. بازگشت فقط snapshot دورریختنی است و اثر پایگاه داده را برنمی‌گرداند.

تاریخ مثال 2026-09-12 و موعد بازبینی 2026-10-18 است. برای ادعای جاری تاریخ واقعی و وضعیت منقضی را حفظ کنید. مالک انسانی یا تأیید سرویس واقعی ارائه نشده است.