عنوان:

‫تبدیلات ضمنی Span در C# 14


نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۰۷/۳۰ ۰۹:۴۵
آدرس: www.dntips.ir
نوع Span از زمان معرفی در C# 7.2، ابزاری قدرتمند برای مدیریت حافظه بدون تخصیص غیرضروری بوده‌است. اما استفاده از آن گاهی با پیچیدگی‌های نحوی همراه بود؛ به‌ویژه هنگام فراخوانی متدها که Span یا ReadOnlySpan می‌پذیرفتند.
با C# 14، مایکروسافت قابلیت‌های جدیدی تحت عنوان تبدیلات ضمنی (implicit conversions) برای Span معرفی کرده‌است که تجربه توسعه را روان‌تر، کد را تمیزتر و عملکرد را بهتر می‌کند. در این مقاله به بررسی این تغییر، مشکلات قبلی و راه‌حل جدید می‌پردازیم.

مسئله: تخصیص حافظه غیرضروری و کد پیچیده
فرض کنید می‌خواهید بخشی از یک رشته را پردازش کنید:
string str = "test string";
string substring = str.Substring(0, 4); // "test"
این کد یک رشته جدید ایجاد می‌کند — حتی اگر فقط به ۴ کاراکتر اول نیاز داشته باشید. این یعنی:
  • تخصیص حافظه جدید
  • کپی داده‌ها
  • فشار بر Garbage Collector
در سناریوهای پرتکرار (مثل پردازش لاگ، پارسینگ داده یا شبکه)، این هزینه‌ها انباشته شده و عملکرد را کاهش می‌دهند.

راه‌حل قدیمی: استفاده صریح ازAsSpan()
برای جلوگیری از تخصیص، از Span استفاده می‌کنیم:
ReadOnlySpan<char> span = str.AsSpan().Slice(0, 4);
این کد:
  • هیچ حافظه جدیدی تخصیص نمی‌دهد
  • مستقیماً به حافظه اصلی اشاره می‌کند
  • سریع‌تر و ایمن‌تر است (به لطف بررسی مرزها)

اما مشکل اینجاست: اگر متدی ReadOnlySpan بپذیرد، نمی‌توانید مستقیماً string یا آرایه را پاس دهید:
public static void ProcessSpan<T>(ReadOnlySpan<T> span) { }

var arr = new int[10];
string text = "hello";

ProcessSpan(arr);        // خطا: نیاز به AsSpan()
ProcessSpan(text);       // خطا: نیاز به AsSpan()

راه‌حل قبلی؟ استفاده صریح از.AsSpan():
ProcessSpan(arr.AsSpan());
ProcessSpan(text.AsSpan());
این تکرار مداوم، کد را شلوغ و خوانایی را کاهش می‌داد.

راه‌حل جدید در C# 14: تبدیلات ضمنی هوشمند
حالا با C# 14، کامپایلر به صورت ضمنی تبدیلاتی را انجام می‌دهد که قبلاً نیاز به دخالت دستی داشت:
ProcessSpan(arr);                    // OK: تبدیل ضمنی به Span<int>
ProcessSpan(charSpan);               // OK
ProcessSpan(readOnlyCharSpan);       // OK
ProcessSpan<char>(text);             // OK: فقط برای string نیاز به <char>

نکات مهم:
  • برای آرایه‌ها (T[]) و Span/ReadOnlySpan موجود، تبدیل کاملاً ضمنی است.
  • برای رشته‌ها (string)، به دلیل ابهام نوع (char یا byte؟)، باید نوع جنریک را صراحتاً مشخص کنید: .

مثال کامل و تمیز
public static void LogFirstFourChars(ReadOnlySpan<char> span)
{
    if (span.Length >= 4)
        Console.WriteLine($"اولین ۴ کاراکتر: {span.Slice(0, 4).ToString()}");
}

// استفاده جدید — تمیز و سریع
string message = "سلام دنیا!";
int[] numbers = { 1, 2, 3, 4, 5 };

LogFirstFourChars<char>(message);  // خروجی: "سلام"
LogFirstFourChars(numbers);        // کامپایل می‌شود (Span<int>)

نتیجه‌گیری
تبدیلات ضمنی Span در C# 14 یک بهبود کوچک اما تأثیرگذار است که:
  • کد را تمیزتر می‌کند
  • خطاهای احتمالی را کاهش می‌دهد
  • توسعه‌دهندگان را به سمت استفاده از الگوهای عملکردمحور سوق می‌دهد
  • بدون هزینه اضافی، بهره‌وری را افزایش می‌دهد
نکته طلایی: هر جا Span یا ReadOnlySpan در امضای متد دیدید، حالا می‌توانید با اطمینان و بدون .AsSpan() از آرایه‌ها و رشته‌ها استفاده کنید — فقط یادتان باشد برای string، نوع جنریک <char> را اضافه کنید.
با این تغییر، نوشتن کدهای بهینه دیگر یک وظیفه اضافی نیست؛ بلکه به بخشی طبیعی از جریان توسعه تبدیل شده است.


مشاهده مطلب اصلی