معرفی Collection Expression Arguments در C# 15.0
نویسنده: وحید نصیری
تاریخ: ۱۴۰۴/۱۱/۲۴ ۱۰:۵۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
with و بازخوردهای جامعه برنامهنویسان میپردازیم.[] مجموعهها را به شکلی بسیار خوانا و مختصر تعریف کنیم:List<int> evenNumbers = [2, 4, 6, 8, 10]; List<int> oddNumbers = [1, 3, 5, 7, 9]; // ترکیب دو لیست و افزودن یک مقدار در ابتدا (Spread Operator) List<int> allNumbers = [0, ..evenNumbers, ..oddNumbers];
HashSet بدهید، مجبور بودید به روش سنتی new List برگردید.args() شناخته میشد، نهایتاً با کلمه کلیدی آشنای with به واقعیت پیوست. این ویژگی اجازه میدهد پارامترهای سازنده (Constructor) را مستقیماً درون کروشه ارسال کنید.// تعیین ظرفیت اولیه لیست برای جلوگیری از تخصیص مجدد حافظه (Resizing) List<int> allNumbers = [with(capacity: 32), 0, ..evenNumbers, ..oddNumbers];
new List ترجمه میشود. همچنین این قابلیت برای مجموعههای پیچیدهتر مانند HashSet که نیاز به تنظیمات خاص دارند، بسیار کاربردی است:// تعریف یک HashSet با نادیده گرفتن کوچک و بزرگی حروف HashSet<string> names = [with(comparer: StringComparer.OrdinalIgnoreCase), "Alice", "Bob"];
with(capacity: ...) استفاده میکنید، کامپایلر دقیقاً میداند که چه مقدار حافظه رزرو کند. در حالتهای ترکیبی (مانند استفاده از .. همراه با مقادیر ثابت)، کامپایلر میتواند محاسبات پیچیدهای انجام دهد تا دقیقترین ظرفیت را از همان ابتدا به سازنده پاس دهد.List یا Dictionary نیست. هر نوع دادهای که الگوی Collection Expression را پیادهسازی کرده باشد، میتواند از طریق متد Create آرگومانهای ارسالی را دریافت و مدیریت کند.[with(capacity: 10), 1, 2] خواناتر از new List نیست..ToList() را به Collection Expressions تبدیل کنند، حتی وقتی که نیازی به آن نیست.withCreate نهفته است. در ادامه، نحوه پیادهسازی این قابلیت را برای یک کلاس سفارشی (Custom Collection) بررسی میکنیم.[with(capacity: 10), 1, 2, 3] استفاده میکنید، کامپایلر به دنبال متدی میگردد که بتواند این آرگومانها را بپذیرد. این کار معمولاً از طریق ویژگی [CollectionBuilder] انجام میشود.MyCoolList داریم:using System.Runtime.CompilerServices;
[CollectionBuilder(typeof(MyCoolListBuilder), nameof(MyCoolListBuilder.Create))]
public class MyCoolList<T> : IEnumerable<T>
{
private readonly T[] _items;
public MyCoolList(ReadOnlySpan<T> items) => _items = items.ToArray();
// متدهای IEnumerable در اینجا پیادهسازی میشوند...
public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_items).GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}Create اکنون میتواند پارامترهای اضافی (مانند capacity) را دریافت کند:public static class MyCoolListBuilder
{
// این متد توسط کامپایلر فراخوانی میشود
public static MyCoolList<T> Create<T>(ReadOnlySpan<T> items, int capacity = 0)
{
Console.WriteLine($"ایجاد لیست با ظرفیت: {capacity}");
// در اینجا میتوانید از ظرفیت برای بهینهسازی حافظه استفاده کنید
return new MyCoolList<T>(items);
}
}capacity در عبارات مجموعهای استفاده کند:// فراخوانی متد Create و پاس دادن مقدار 50 به پارامتر capacity MyCoolList<int> myNumbers = [with(capacity: 50), 10, 20, 30];
with(capacity: ...) استفاده کنید، کامپایلر آن را مستقیماً به متد سازنده میفرستد.List.Add که در هر مرحله ممکن است باعث جابجایی حافظه شود، در اینجا دادهها میتوانند به صورت یک ReadOnlySpan بسیار سریع و یکباره به مجموعه منتقل شوند..Add().capacity):1, 2, 3) را میشمارد.spread operator (مثل ..others) استفاده کرده باشید، طول آن را محاسبه میکند.with(capacity: ...)، دقیقاً همان مقدار فضا را یکبار رزرو میکند.ReadOnlySpan) در حافظه مینویسد.| روش پیادهسازی | زمان اجرا (میانگین) | تخصیص حافظه (Allocated) | تعداد دفعات GC |
new List | ۱۲۰ نانوثانیه | ۴۵۰ بایت | ۲ |
[with(capacity: 100), ..data] | ۸۵ نانوثانیه | ۴۰۰ بایت | ۰ |
DictionaryDictionary است. ایجاد دیکشنری با ظرفیت مشخص، از Collision (تداخل هشها) جلوگیری میکند و سرعت جستجو (Look-up) را در ابتدای کار بالا میبرد:// بهینهسازی دیکشنری برای ۱۰۰۰ آیتم از همان لحظه تولد!
Dictionary<int, string> lookup = [with(capacity: 1000), {1, "Active"}, {2, "Pending"}];capacitywith(capacity: ...) استفاده کنید. این کار از "تخصیص مجدد" (Re-allocation) حافظه جلوگیری میکند.with(comparer: ...) بسیار تمیزتر از روش قدیمی است. این کار به ویژه زمانی که میخواهید مقایسههای رشتهای را به صورت OrdinalIgnoreCase انجام دهید، خوانایی کد را به شدت بالا میبرد.// روش مدرن و خوانا در C# 15 Dictionary<string, int> counts = [with(comparer: StringComparer.OrdinalIgnoreCase), ["Key", 1]];
with(capacity: 3) تاثیری در عملکرد ندارد و فقط کد را شلوغ میکند. کامپایلر سیشارپ به اندازه کافی هوشمند است که برای تعداد کمی از آیتمهای ثابت، بهینهترین کد را تولید کند..ToList()) کاملاً خواناست و در یک بخش حساس از نظر پرفورمنس نیست، نیازی به تغییر اجباری به with ندارید. اجازه ندهید ابزارهای تحلیل کد، خوانایی منطق برنامه شما را فدای سینتکسهای جدید کنند.