new Employee { Name = "Sara", DepartmentId = 1, Salary = 5500000 },
new Employee { Name = "Reza", DepartmentId = 2, Salary = 4500000 },
new Employee { Name = "Neda", DepartmentId = 1, Salary = 6000000 },
new Employee { Name = "Hossein", DepartmentId = 3, Salary = 4800000 }
};
// GroupJoin - هر دپارتمان با لیست کارمندانش
var departmentsWithEmployees = departments.GroupJoin(
employees,
dept => dept.Id,
emp => emp.DepartmentId,
(dept, empList) => new
{
DepartmentName = dept.Name,
Employees = empList,
EmployeeCount = empList.Count(),
TotalSalary = empList.Sum(e => e.Salary)
}
);
Console.WriteLine("=== Departments with Employees (GroupJoin) ===");
foreach (var dept in departmentsWithEmployees)
{
Console.WriteLine($"\nDepartment: {dept.DepartmentName}");
Console.WriteLine($" Total Employees: {dept.EmployeeCount}");
Console.WriteLine($" Total Salary: {dept.TotalSalary:C}");
Console.WriteLine(" Employees:");
foreach (var emp in dept.Employees)
{
Console.WriteLine($" - {emp.Name}: {emp.Salary:C}");
}
}
// Query Syntax GroupJoin
var queryGroupJoin = from dept in departments
join emp in employees
on dept.Id equals emp.DepartmentId
into empGroup
select new
{
DepartmentName = dept.Name,
Employees = empGroup,
AverageSalary = empGroup.Average(e => e.Salary)
};
Console.WriteLine("\n=== Average Salary by Department ===");
foreach (var dept in queryGroupJoin)
{
Console.WriteLine($"{dept.DepartmentName}: Average Salary = {dept.AverageSalary:C}");
}
---
⏱️ قسمت 3: SelectMany (صاف کردن لیستهای تو در تو)
SelectMany یک لیست از لیستها را به یک لیست تخت تبدیل میکند.
csharp
// مثال 1: لیست کلاسها با لیست دانشجویان
class ClassRoom
{
public string ClassName { get; set; }
public List<string> Students { get; set; }
}
List<ClassRoom> schools = new List<ClassRoom>
{
new ClassRoom
{
ClassName = "Class A",
Students = new List<string> { "Ali", "Reza", "Sara" }
},
new ClassRoom
{
ClassName = "Class B",
Students = new List<string> { "Neda", "Hossein" }
},
new ClassRoom
{
ClassName = "Class C",
Students = new List<string> { "Zahra", "Mohammad", "Fatemeh" }
}
};
// بدون SelectMany (دست و پاگیر)
Console.WriteLine("=== Without SelectMany ===");
foreach (var classroom in schools)
{
foreach (var student in classroom.Students)
{
Console.WriteLine($"{classroom.ClassName} - {student}");
}
}
// با SelectMany (ساده و زیبا)
var allStudents = schools.SelectMany(s => s.Students);
Console.WriteLine("\n=== All Students (Flat List) ===");
Console.WriteLine(string.Join(", ", allStudents));
// با SelectMany و همراه با اطلاعات کلاس
var studentsWithClass = schools.SelectMany(
classroom => classroom.Students,
(classroom, student) => new { Classroom = classroom.ClassName, Student = student }
);
Console.WriteLine("\n=== Students with Class Info ===");
foreach (var item in studentsWithClass)
{
Console.WriteLine($"{item.Student} is in {item.Classroom}");
}
مثال دیگر: اعداد درون لیست
csharp
List<int[]> numberGroups = new List<int[]>
{
new int[] { 1, 2, 3 },
new int[] { 4, 5 },
new int[] { 6, 7, 8, 9 }
};
var flatNumbers = numberGroups.SelectMany(g => g);
Console.WriteLine($"\nFlat numbers: {string.Join(", ", flatNumbers)}"); // 1,2,3,4,5,6,7,8,9
---
⏱️ قسمت 4: Distinct, Union, Intersect, Except
این متدها برای کار با مجموعهها (Set Operations) استفاده میشوند.
csharp
List<int> listA = new List<int> { 1, 2, 3, 4, 5, 5, 6 };
List<int> listB = new List<int> { 4, 5, 6, 7, 8, 9 };
// Distinct - حذف تکراریها
var uniqueA = listA.Distinct();
Console.WriteLine($"Distinct A: {string.Join(", ", uniqueA)}"); // 1,2,3,4,5,6
// Union - اجتماع (بدون تکرار)
var union = listA.Union(listB);
Console.WriteLine($"Union: {string.Join(", ", union)}"); // 1,2,3,4,5,6,7,8,9
// Intersect - اشتراک
var intersect = listA.Intersect(listB);
Console.WriteLine($"Intersect: {string.Join(", ", intersect)}"); // 4,5,6
// Except - تفاضل (موجود در A ولی نه در B)
var except = listA.Except(listB);
Console.WriteLine($"Except (A - B): {string.Join(", ", except)}"); // 1,2,3
مثال با اشیاء (استفاده از IEqualityComparer):
csharp
class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
List<Product> products1 = new List<Product>
{
new Product { Id = 1, Name = "Laptop" },
new Product { Id = 2, Name = "Mouse" },
new Product { Id = 3, Name = "Keyboard" }
};
List<Product> products2 = new List<Product>
{
new Product { Id = 2, Name = "Mouse" },
new Product { Id = 3, Name = "Keyboard" },
new Product { Id = 4, Name = "Monitor" }
};
// Intersect با اشیاء (نیاز به مقایسهکننده)
var commonProducts = products1.Intersect(products2, new ProductComparer());
Console.WriteLine("\n=== Common Products ===");
foreach (var p in commonProducts)
{
Console.WriteLine(p.Name);
}
class ProductComparer : IEqualityComparer<Product>
{
public bool Equals(Product x, Product y)
{
return x.Id == y.Id;
}
public int GetHashCode(Product obj)
{
return obj.Id.GetHashCode();
}
}
---
⏱️ قسمت 5: متد Zip (ترکیب دو لیست)
Zip دو لیست را عنصر به عنصر با هم ترکیب میکند.
csharp
List<string> names = new List<string> { "Ali", "Sara", "Reza", "Neda" };
List<int> ages = new List<int> { 25, 30, 22, 28 };
List<string> cities = new List<string> { "Tehran", "Shiraz", "Isfahan" };
// Zip دو تایی
var nameAge = names.Zip(ages, (name, age) => $"{name} is {age} years old");
Console.WriteLine("=== Zip (Name + Age) ===");
foreach (var item in nameAge)
{
Console.WriteLine(item);
}
// Zip سه تایی (با استفاده از Zip تو در تو)
var nameAgeCity = names
.Zip(ages, (name, age) => new { name, age })
.Zip(cities, (combined, city) => $"{combined.name} is {combined.age} years old from {city}");
Console.WriteLine("\n=== Zip (Name + Age + City) ===");
foreach (var item in nameAgeCity)
{
Console.WriteLine(item);
}
---
⏱️ قسمت 6: اجرای دیررس (Deferred) در مقابل اجرای فوری (Immediate)
csharp
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// اجرای دیررس (Deferred) - تا زمانی که به نتیجه نیاز نباشد، اجرا نمیشود
var deferredQuery = numbers.Where(n =>
{
Console.WriteLine($"Checking {n}");
return n > 2;
});
Console.WriteLine("Query defined, but not executed yet...");
Console.WriteLine("Now executing with ToList():");
var resultList = deferredQuery.ToList(); // اینجا اجرا میشود
// اجرای فوری (Immediate) - بلافاصله اجرا میشود
Console.WriteLine("\nImmediate execution:");
var immediateResult = numbers
.Where(n => n > 2)
.ToList(); // بلافاصله اجرا میشود
متدهایی که اجرای فوری دارند:
· ToList(), ToArray(), ToDictionary(), ToLookup()
· Count(), Sum(), Average(), Min(), Max()
· First(), FirstOrDefault(), Single(), SingleOrDefault()
csharp
List<int> data = new List<int> { 10, 20, 30, 40, 50 };
// اجرای دیررس
var deferred = data.Where(x => x > 25);
data.Add(60); // اضافه کردن بعد از تعریف query
Console.WriteLine($"Deferred: {string.Join(", ", deferred)}"); // 30,40,50,60
// اجرای فوری
var immediate = data.Where(x => x > 25).ToList();
data.Add(70); // اضافه کردن بعد از ToList
Console.WriteLine($"Immediate: {string.Join(", ", immediate)}"); // 30,40,50,60 (70 اضافه نمیشود)
---
✅ تمرین نهایی درس شانزدهم
برنامهای بنویسید که:
1. دو لیست Customers و Orders بسازد
2. با Join، سفارشهای هر مشتری را نمایش دهد
3. با GroupJoin، مشتریانی که سفارش ندارند را هم نشان دهد
4. با SelectMany، لیست تمام محصولات سفارش داده شده را به صورت تخت نمایش دهد
5. با Union و Intersect، محصولات مشترک بین دو ماه را پیدا کند
csharp
using System;
using System.Collections.Generic;
using System.Linq;
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
}
class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
public List<string> Products { get; set; }
}
class Program
{
static void Main()
{
// دادهها
List<Customer> customers = new List<Customer>
{
new Customer { Id = 1, Name = "Ali Mohammadi", City = "Tehran" },
new Customer { Id = 2, Name = "Sara Ahmadi", City = "Shiraz" },
new Customer { Id = 3, Name = "Reza Karimi", City = "Tehran" },
new Customer { Id = 4, Name = "Neda Hosseini", City = "Isfahan" }
};
List<Order> orders = new List<Order>
{
new Order { Id = 101, CustomerId = 1, Date = DateTime.Now.AddDays(-10), Amount = 1500000,
Products = new List<string> { "Laptop", "Mouse" } },
new Order { Id = 102, CustomerId = 1, Date = DateTime.Now.AddDays(-5), Amount = 250000,
Products = new List<string> { "Keyboard" } },
new Order { Id = 103, CustomerId = 2, Date = DateTime.Now.AddDays(-3), Amount = 85000,
Products = new List<string> { "Book" } },
new Order { Id = 104, CustomerId = 3, Date = DateTime.Now.AddDays(-1), Amount = 5500000,
Products = new List<string> { "Monitor", "Mouse", "Keyboard" } }
};
// 1. Join - سفارشهای هر مشتری
var customerOrders = customers.Join(
orders,
c => c.Id,
o => o.CustomerId,
(c, o) => new { CustomerName = c.Name, OrderId = o.Id, o.Amount, o.Date }
).OrderBy(x => x.CustomerName);
Console.WriteLine("=== Customer Orders (Join) ===");
foreach (var item in customerOrders)
{
Console.WriteLine($"{item.CustomerName}: Order #{item.OrderId} - {item.Amount:C} on {item.Date.ToShortDateString()}");
}
// 2. GroupJoin - مشتریان با لیست سفارشهایشان (حتی بدون سفارش)
var customersWithOrders = customers.GroupJoin(
orders,
c => c.Id,
o => o.CustomerId,
(c, orderList) => new
{
CustomerName = c.Name,
City = c.City,
Orders = orderList,
TotalSpent = orderList.Sum(o => o.Amount),
OrderCount = orderList.Count()
}
);
Console.WriteLine("\n=== Customers with Orders (GroupJoin) ===");
foreach (var c in customersWithOrders)
{
Console.WriteLine($"{c.CustomerName} ({c.City}): {c.OrderCount} orders, Total: {c.TotalSpent:C}");
foreach (var order in c.Orders)
{
Console.WriteLine($" - Order #{order.Id}: {order.Amount:C}");
}
}
// 3. SelectMany - لیست تمام محصولات سفارش داده شده
var allProducts = orders.SelectMany(o => o.Products).Distinct();
Console.WriteLine($"\n=== All Products Ordered ===");
Console.WriteLine(string.Join(", ", allProducts));
// 4. محصولات سفارش داده شده در دو ماه مختلف (مثال با فرض دو دسته سفارش)
var lastWeekProducts = orders
.Where(o => o.Date >= DateTime.Now.AddDays(-7))
.SelectMany(o => o.Products)
.Distinct();
var olderProducts = orders
.Where(o => o.Date < DateTime.Now.AddDays(-7))
.SelectMany(o => o.Products)
.Distinct();
Console.WriteLine("\n=== Products in Last Week ===");
Console.WriteLine(string.Join(", ", lastWeekProducts));
Console.WriteLine("\n=== Products Older than Week ===");
Console.WriteLine(string.Join(", ", olderProducts));
// 5. Union و Intersect
var allUniqueProducts = lastWeekProducts.Union(olderProducts);
var commonProducts = lastWeekProducts.Intersect(olderProducts);
Console.WriteLine($"\n=== All Unique Products: {string.Join(", ", allUniqueProducts)}");
Console.WriteLine($"=== Common Products: {string.Join(", ", commonProducts)}");
// 6. مشتریانی که بیش از یک سفارش دارند
var repeatCustomers = customersWithOrders.Where(c => c.OrderCount > 1);
Console.WriteLine("\n=== Customers with Multiple Orders ===");
foreach (var c in repeatCustomers)
{
Console.WriteLine($"{c.CustomerName}: {c.OrderCount} orders");
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
}
}
`--- 📌 جمعبندی درس شانزدهم دسته متدها کاربرد Join Join, GroupJoin ترکیب دو لیست صاف کردن SelectMany تبدیل لیست تو در تو به تخت مجموعه Distinct, Union, Intersect, Except عملیات مجموعهای ترکیب Zip ترکیب عنصر به عنصر اجرا Deferred, Immediate زمان اجرای query نکات مهم: · Join = INNER JOIN در SQL · GroupJoin = LEFT JOIN + GROUP BY · SelectMany = فلَت کردن لیستها · بیشتر متدهای LINQ اجرای دیررس دارند · متدهای ToList, Count, Sum اجرای فوری دارند --- 🧪 تمرین برای شما 1. دو لیست از دانشجویان و نمرات بسازید و با Join ترکیب کنید 2. لیستی از سفارشات با آیتمهایشان بسازید و با SelectMany همه آیتمها را لیست کنید 3. با Union و Intersect، اعداد مشترک بین دو آرایه را پیدا کنید --- 🎉 تبریک! شما LINQ را کامل یاد گرفتید! درسهای LINQ تمام شد: درس عنوان درس 14 LINQ قسمت 1 – متدهای پایه (Where, Select, First) درس 15 LINQ قسمت 2 – مرتبسازی، گروهبندی، محاسبات درس 16 LINQ قسمت 3 – Join و عملگرهای پیشرفته --- اگر این درس را کامل فهمیدید، میتوانیم درس بعدی را شروع کنیم. مباحث پیشنهادی برای ادامه: · درس 17: کار با فایلها (File I/O) · درس 18: Async/Await (برنامهنویسی نامن synchronous) · درس 19: Attribute و Reflection · درس 20: Entity Framework (کار با دیتابیس) بگویید کدام مبحث را میخواهید. سوالی بود بپرسید.