هر نسخهی جدید از زبان #C ابزارها و قابلیتهای قدرتمندتری را در اختیار ما قرار میدهد. یکی از مهمترین چالشها در توسعه کتابخانهها، هدف قرار دادن چندین فریمورک (Multi-Targeting) است، بهویژه زمانی که میخواهیم از APIهای جدید استفاده کنیم اما همچنان پشتیبانی از نسخههای قدیمیتر .NET را حفظ کنیم. در این مقاله، قصد داریم به معرفی و بررسی قابلیت جدید اعضای گسترشدهنده (Extension Members) در C# 14 بپردازیم و ببینیم چگونه این قابلیت، مفهوم پُلیفیلها (Polyfills) را متحول میکند.
توضیح مسئله: چالشهای چند-هدفگذاری (Multi-Targeting)
هنگامیکه یک کتابخانه .NET مینویسیم، اغلب نیاز داریم که چندین TFM (Target Framework Moniker) را هدف قرار دهیم، مثلاً .NET 6 و .NET Standard 2.0. هدفگذاری چندین فریمورک به کتابخانه ما اجازه میدهد تا توسط طیف وسیعتری از برنامهها استفاده شود. با این حال، استفاده از APIهای مدرن در نسخههای جدیدتر، چالشهایی را در نسخههای قدیمیتر ایجاد میکند.
راهحل قدیمی:
قبل از C# 14 و ابزارهای نوین، توسعهدهندگان مجبور بودند برای مدیریت این تفاوتها، به دستورات پیشپردازنده مانند #if متوسل شوند.
به عنوان مثال، برای استفاده از متد همزمان (Process.WaitForExitAsync) که در نسخههای جدیدتر .NET معرفی شده است، در مقابل متد ناهمزمان قدیمیتر (Process.WaitForExit)، کدها بسیار خوانایی خود را از دست میدادند:
public async Task Sample()
{
var process = Process.Start("sample.exe");
#if NET6_0_OR_GREATER
// API جدید
await process.WaitForExitAsync();
#else
// API قدیمی
process.WaitForExit();
#endif
}استفاده گسترده از #if، خوانایی و نگهداری کد را بهشدت کاهش میدهد.
نقش پُلیفیلها در راهحل قدیمی:
پُلیفیل در اصل قطعهای از کد است که عملکرد مشابه یک API جدید را برای محیطهای قدیمیتر که آن API در آنها موجود نیست، فراهم میکند. تا پیش از C# 14، پُلیفیلها میتوانستند:
- انواع جدید (کلاسها، اینترفیسها، ساختارها، Enumها) را تعریف کنند.
- متدهای نمونه (Instance Methods) جدید را از طریق متدهای گسترشدهنده (Extension Methods) اضافه کنند.
اما یک محدودیت بزرگ وجود داشت: پُلیفیل کردن متدهای ایستا (Static Methods) و ویژگیهای (Properties) جدید بر روی انواع موجود، امکانپذیر نبود.
راهحل جدید: اعضای گسترشدهنده در C# 14
C# 14 با معرفی اعضای گسترشدهنده، این محدودیت را از بین میبرد. این قابلیت، سطح گسترشپذیری #C را بهطور قابل توجهی افزایش داده است.
اعضای گسترشدهنده به شما اجازه میدهند که علاوه بر متدهای نمونه، ویژگیهای گسترشدهنده (Extension Properties) و متدهای ایستای گسترشدهنده (Extension Static Methods) را نیز به انواع موجود اضافه کنید.
تعریف اعضای گسترشدهنده:
برای تعریف اعضای گسترشدهنده، از کلمه کلیدی extension به جای static class و پارامتر this استفاده میشود.
اهمیت اعضای گسترشدهنده برای پُلیفیلها:
این قابلیت به کتابخانههای پُلیفیل اجازه میدهد تا تجربه کاملتری را ارائه دهند. اکنون میتوان APIهای ایستایی را که در نسخههای جدید .NET معرفی شدهاند، دقیقاً با همان امضا برای نسخههای قدیمیتر شبیهسازی کرد.
مثال دنیای واقعی: متد ArgumentNullException.ThrowIfNull
متد ایستای ArgumentNullException.ThrowIfNull که در .NET 6 معرفی شد، نمونهای عالی است. این متد راهی مختصر برای اعتبارسنجی عدم Null بودن یک پارامتر ارائه میدهد:
public void ProcessData(string data)
{
// API تمیز و مدرن
ArgumentNullException.ThrowIfNull(data);
// ادامه پردازش داده...
}قبل از C# 14، هیچ راهی برای اضافه کردن ThrowIfNull به عنوان یک متد ایستا بر روی نوع ArgumentNullException در یک کتابخانه پُلیفیل وجود نداشت.
پیادهسازی با استفاده از اعضای گسترشدهنده:
با استفاده از متدهای ایستای گسترشدهنده در C# 14، کتابخانه پُلیفیل میتواند دقیقاً همین API را برای فریمورکهای هدف قدیمیتر فراهم کند:
// تعریف اعضای گسترشدهنده برای ArgumentNullException
static class PolyfillExtensions
{
extension(ArgumentNullException)
{
// متد ایستای گسترشدهنده
public static void ThrowIfNull(
[NotNull] object? argument,
[CallerArgumentExpression(nameof(argument))] string? paramName = null)
{
if (argument is null)
throw new ArgumentNullException(paramName);
}
}
}این پُلیفیل تضمین میکند که کد شما، بدون توجه به TFM هدف، از ArgumentNullException.ThrowIfNull استفاده کند، که خوانایی و نگهداری کد را به طور چشمگیری بهبود میبخشد.
💡 نکته: اعضای گسترشدهنده نه تنها برای پُلیفیلها، بلکه در سناریوهایی که میخواهید برای افزایش خوانایی دامنه خاص (Domain-Specific Readability) به یک نوع عمومی (Type) قابلیتهای جدیدی اضافه کنید، بسیار کاربردی هستند. مثلاً، تعریف یک ویژگی گسترشدهنده برای دسترسی سادهتر به یک عضو داخلی پیچیده در یک Struct سیستمی.
نتیجهگیری
اعضای گسترشدهنده در C# 14 یک گام بزرگ رو به جلو در تکامل زبان هستند. این قابلیت، محدودیتهای قدیمی متدهای گسترشدهنده را برداشته و به توسعهدهندگان این امکان را میدهد که متدهای ایستا و ویژگیها را نیز گسترش دهند.
برای ما که در حوزه توسعه کتابخانه فعالیت میکنیم، این یعنی:
- تجربهی پُلیفیل کاملتر: میتوانیم APIهای جدید را با حفظ امضای اصلیشان (چه نمونهای و چه ایستا) به فریمورکهای هدف قدیمیتر برگردانیم.
- کاهش چشمگیر دستورات
#if: با استفاده از کتابخانههای پُلیفیل مدرن، میتوانیم کدی تمیزتر، قابل نگهداریتر و یکپارچهتر بنویسیم که از APIهای جدید در تمامی نسخههای پشتیبانیشده استفاده میکند.
بهرهگیری از این قابلیت جدید و استفاده از کتابخانههای پُلیفیل قوی و بهروز، رمز موفقیت در توسعه یک کتابخانه .NET با پشتیبانی گسترده و کدی تمیز است.