سادهسازی اتصالات LINQ بدون انتخابگر نتیجه (Result Selector) در NET 11.
نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۵/۰۵ ۰۸:۲۰
آدرس: www.dntips.ir
Join توسعهدهندگان را ملزم میکرد که همواره پارامتر چهارم یا همان انتخابگر نتیجه (resultSelector) را برای نگاشت خروجی مشخص کنند؛ امری که در اکثر موارد منجر به نوشتن کدهای تکراری و عبارات چندگانه (Tuples) میشد. در این مقاله، به بررسی بهبود جدید ارائه شده در .NET 11 برای سادهسازی متد Join، نحوه حذف تشریفات اضافی کد (Boilerplate)، بررسی دقیق رفتار کامپایلر و اثرات آن بر تمیزی کد میپردازیم.Enumerable.Join، شامل یک الزام تاریخی بود: ارائه صریح تابع انتخابگر خروجی. در سناریوهایی که هدف صرفاً دریافت جفتهای منطبق از دو مجموعه بدون تغییر ساختار اولیه بود، توسعهدهندگان مجبور به تعریف دستی یک Tuple واسط بودند. .NET 11 با معرفی Overloadهای جدید و هوشمندتر، این تشریفات کدنویسی را برطرف کرده است.Product (کالا) و Order (سفارش) را در نظر بگیرید:public class Product
{
public required int ID { get; init; }
public required string Name { get; init; }
}
public class Order
{
public required int OrderID { get; init; }
public required int ProductID { get; init; }
public required int Quantity { get; init; }
}Product[] products = [
new() { ID = 1, Name = "Mango" },
new() { ID = 2, Name = "Banana" },
new() { ID = 3, Name = "Potato" },
new() { ID = 4, Name = "Cabbage" }
];
Order[] orders = [
new() { OrderID = 1, ProductID = 1, Quantity = 10 },
new() { OrderID = 1, ProductID = 2, Quantity = 13 },
new() { OrderID = 2, ProductID = 3, Quantity = 5 },
new() { OrderID = 2, ProductID = 4, Quantity = 8 }
];resultSelector ارسال میشد:var result = products.Join(
orders,
product => product.ID,
order => order.ProductID,
(product, order) => (product, order) // انتخابگر نتیجه اجباری (ValueTuple)
);(product, order) => (product, order) هیچ کار ساختاری یا محاسباتی خاصی انجام نمیداد و تنها برای تبدیل خروجی به یک ValueTuple نوشته میشد.Join، نیازی به نگارش پارامتر چهارم نیست:var newResult = products.Join(
orders,
product => product.ID,
order => order.ProductID
);(Product product, Order order) خواهد بود و پیمایش آن دقیقاً یکسان است:foreach (var (product, order) in newResult)
{
Console.WriteLine($"Product: {product.Name}, Quantity: {order.Quantity}");
}Product: Mango, Quantity: 10 Product: Banana, Quantity: 13 Product: Potato, Quantity: 5 Product: Cabbage, Quantity: 8
join استفاده میشد، میتوانستید خروجی دلخواه را انتخاب کنید. اما در Method Syntax الزام وجود پارامتر چهارم همواره حس میشد. بهبود .NET 11 باعث شده است تا Method Syntax از نظر سادگی به همان میزان از تمیزی دست یابد.ValueTuple) را تولید میکند که متد قبلی با انتخابگر دستی تولید میکرد. بنابراین این تغییر هیچ افت عملکردی ایجاد نمیکند و با حذف نیاز به ارزیابی یک Delegate اضافه، کدهای CIL تمیزتری تولید میشود.Zip مشاهده شده بود.| ویژگی | تا پیش از .NET 11 | در .NET 11 Preview |
| تعداد پارامترهای اجباری Join | ۴ پارامتر | ۳ پارامتر |
| نیاز به تعریف Manual Tuple | بله (a, b) => (a, b) | خیر (تولید خودکار ValueTuple) |
| خوانایی کد (Readability) | همراه با شلوغی بصری | تمیز و مستقیم |
| نوع خروجی (Type Signature) | IEnumerable<(T1, T2)> | IEnumerable<(T1, T2)> |
نکته: اگر پس از انجام Join قصد داشته باشید مستقیماً یک DTO جدید بسازید یا فیلد خاصی را استخراج کنید، همچنان میتوانید از Overload چهارپارامتری کلاسیک استفاده کنید. متد جدید صرفاً برای سناریوی شایع Projection کامل دو عنصر ارائه شده است.
LINQ Join گامی کوچک اما ارزشمند در جهت تمیزتر شدن کدهای C# و کاهش تشریفات غیرضروری است. با حذف نیاز اجباری به resultSelector هنگام بازگرداندن ساده جفتهای منطبق، توسعهدهندگان میتوانند کدهایی خواناتر، کوتاهتر و گویاتر بنویسند.