ساده سازی انتخاب های پیچیده با روش های LINQ و Extension

Summarize this content to 400 words in Persian Lang
هنگام کار با مجموعهها، اغلب نیاز داریم که آیتم خاصی را پیدا کنیم که شرایط خاصی را برآورده کند – مانند یافتن بالاترین، پایینترین یا مناسبترین عنصر بر اساس منطق سفارشی. در حالی که حلقههای سنتی میتوانند چنین مشکلاتی را حل کنند، اغلب پرمخاطب هستند و نگهداری از آنها سختتر است. روش های LINQ و افزونه روشی زیباتر، کارآمدتر و قابل استفاده مجدد را برای حل این مشکلات ارائه می دهند.
در این مقاله، نحوه ساده سازی چنین سناریوهایی را با استفاده از LINQ بررسی خواهیم کرد Aggregate روش و منطق را در یک روش پسوند قابل استفاده مجدد به نام کپسوله کنید WithMaximum. ما راه حل را مرحله به مرحله تجزیه می کنیم و تمام جزئیات را توضیح می دهیم تا درک آن آسان شود.
سناریو: انتخاب بهترین کارمند
تصور کنید ما سیستمی داریم که وظایفی را به کارمندان اختصاص می دهد. هر کارمند دارای:
Name: نام آنها
SkillLevel: معیاری برای سنجش مهارت آنها.
IsAvailable: آیا آنها برای یک کار در دسترس هستند.
هدف این است که یک وظیفه را به ماهرترین کارمند موجود.
مرحله 1: رویکرد پایه LINQ
یک راه حل معمول LINQ ممکن است به شکل زیر باشد:
public Employee GetBestCandidate(IEnumerable<Employee> employees)
{
return employees
.Where(e => e.IsAvailable)
.OrderByDescending(e => e.SkillLevel)
.FirstOrDefault();
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
Where: کارمندان غیرقابل دسترس را فیلتر می کند.
OrderByDescending: مرتب سازی بر اساس SkillLevel به ترتیب نزولی
FirstOrDefault: اولین کارمند را از لیست مرتب شده (کسی که بالاترین سطح مهارت را دارد) انتخاب می کند.
این رویکرد کار می کند اما ناکارآمد است زیرا کل مجموعه را مرتب می کند. مرتب سازی دارای پیچیدگی زمانی (O(N \log N)) است که اگر فقط به نتیجه برتر نیاز داشته باشید می تواند غیرضروری باشد.
مرحله 2: راه حل کارآمد با Aggregate
ما می توانیم با اجتناب از مرتب سازی کارایی را بهبود بخشیم. LINQ Aggregate متد به ما امکان می دهد مجموعه را در یک پاس ((O(N))) اسکن کنیم و آن را برای این کار سریعتر می کند.
public Employee GetBestCandidate(IEnumerable<Employee> employees)
{
return employees
.Where(e => e.IsAvailable)
.Aggregate((currentBest, next) =>
currentBest == null || next.SkillLevel > currentBest.SkillLevel ? next : currentBest);
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
Aggregate:
مجموعه را به یک ارزش واحد کاهش می دهد (ماهرترین کارمند).
هر کارمند را با هم مقایسه می کند SkillLevel به بهترین حالت فعلی
با این حال، Aggregate می تواند پرمخاطب و خواندن آن سخت تر باشد، به خصوص برای شرایط پیچیده. برای بهبود خوانایی، این منطق را در یک روش پسوند قابل استفاده مجدد قرار می دهیم.
مرحله 3: کپسوله کردن منطق در WithMaximum
این WithMaximum متد extension به ما اجازه می دهد تا عنصری را با بالاترین مقدار بر اساس یک معیار سفارشی پیدا کنیم. بیایید آن را تجزیه کنیم خط به خط به طور کامل درک کنید که چگونه کار می کند.
کد
public static class EnumerableExtensions
{
public static T WithMaximum<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> selector)
where T : class
where TKey : IComparable<TKey>
{
var tuples = source.Select(item => (Item: item, Key: selector(item)));
var result = tuples.Aggregate(
seed: (Item: null as T, Key: default(TKey)),
(currentBest, next) =>
currentBest.Item == null || next.Key.CompareTo(currentBest.Key) > 0
? next
: currentBest
);
return result.Item;
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
تفکیک گام به گام
اعلامیه روش
public static T WithMaximum<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> selector)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
روش گسترش: this کلمه کلیدی این را به یک روش پسوندی تبدیل می کند IEnumerable. می توانید آن را مستقیماً در هر دنباله ای فراخوانی کنید (مثلاً employees.WithMaximum(…)).
پارامترهای عمومی:
T: نوع عناصر موجود در مجموعه (به عنوان مثال، Employee).
TKey: نوع مقدار مورد استفاده برای مقایسه (به عنوان مثال، int برای SkillLevel).
Func selector:
تابعی که یک عنصر از نوع را می گیرد T و کلید نوع خود را برمی گرداند TKey (مثلا e => e.SkillLevel).
محدودیت های عمومی
where T : class
where TKey : IComparable<TKey>
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
T : class:
تضمین می کند T یک نوع مرجع است، بنابراین null می تواند به عنوان دانه اولیه استفاده شود.
TKey : IComparable:
تضمین می کند TKey پشتیبانی از مقایسه (CompareTo) تا بتوانیم حداکثر را تعیین کنیم.
کلیدهای پیش محاسبه با Select
var tuples = source.Select(item => (Item: item, Key: selector(item)));
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
هدف: کلید هر عنصر را از قبل محاسبه می کند تا از چندین بار محاسبه مجدد آن جلوگیری کند.
نتیجه: هر عنصر را به یک تاپل تبدیل می کند که شامل:
Item: عنصر اصلی.
Key: مقدار محاسبه شده (مثلا SkillLevel).
مثال:اگر source شامل:
Alice (SkillLevel: 85)
Bob (SkillLevel: 90)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
و selector است e => e.SkillLevel، سپس tuples می شود:
[
(Item: Alice, Key: 85),
(Item: Bob, Key: 90)
]
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
تجمیع با Aggregate
var result = tuples.Aggregate(
seed: (Item: null as T, Key: default(TKey)),
(currentBest, next) =>
currentBest.Item == null || next.Key.CompareTo(currentBest.Key) > 0
? next
: currentBest
);
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
seed:
ارزش اولیه: (Item: null, Key: default(TKey)).
Item: شروع به عنوان null.
Key: به عنوان مقدار پیش فرض شروع می شود TKey (مثلا 0 برای int).
منطق:
مقایسه کنید next.Key با currentBest.Key:
اگر currentBest.Item است null یا next.Key بزرگتر است، به روز رسانی currentBest به next.
در غیر این صورت نگه دارید currentBest.
مثال:
تکرار 1:
currentBest = (null, 0)، next = (Alice, 85)
currentBest است null، بنابراین currentBest = (Alice, 85).
تکرار 2:
currentBest = (Alice, 85)، next = (Bob, 90)
90 > 85، بنابراین currentBest = (Bob, 90).
نتیجه را برگردانید
return result.Item;
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
را استخراج کنید Item (شیء اصلی) از تاپل نهایی.
در این صورت باز می گردد Bob.
مرحله 4: استفاده از WithMaximum
اکنون روش اصلی بسیار ساده تر می شود:
public Employee GetBestCandidate(IEnumerable<Employee> employees)
{
return employees
.Where(e => e.IsAvailable)
.WithMaximum(e => e.SkillLevel);
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
مثال کامل
کلاس کارمند
public class Employee
{
public string Name { get; set; }
public int SkillLevel { get; set; }
public bool IsAvailable { get; set; }
public override string ToString()
{
return $”{Name} (Skill: {SkillLevel}, Available: {IsAvailable})”;
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
روش گسترش
public static class EnumerableExtensions
{
public static T WithMaximum<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> selector)
where T : class
where TKey : IComparable<TKey>
{
var tuples = source.Select(item => (Item: item, Key: selector(item)));
var result = tuples.Aggregate(
seed: (Item: null as T, Key: default(TKey)),
(currentBest, next) =>
currentBest.Item == null || next.Key.CompareTo(currentBest.Key) > 0
? next
: currentBest
);
return result.Item;
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
برنامه اصلی
class Program
{
static void Main(string[] args)
{
var employees = new List<Employee>
{
new Employee { Name = “Alice”, SkillLevel = 85, IsAvailable = true },
new Employee { Name = “Bob”, SkillLevel = 90, IsAvailable = false },
new Employee { Name = “Charlie”, SkillLevel = 78, IsAvailable = true },
new Employee { Name = “Diana”, SkillLevel = 92, IsAvailable = true }
};
var bestCandidate = employees.GetBestCandidate();
Console.WriteLine(“Best Candidate:”);
Console.WriteLine(bestCandidate
?.ToString() ?? “No available employees.”);
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
خروجی
Best Candidate:
Diana (Skill: 92, Available: True)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
خوراکی های کلیدی
کارایی: WithMaximum از مرتبسازی غیرضروری اجتناب میکند و مجموعه را در یک پاس اسکن میکند.
قابلیت استفاده مجدد: این روش برای هر نوع منطق جمع آوری و مقایسه قابل استفاده است.
خوانایی: منطق پیچیده کپسوله شده است و کد مصرف کننده را تمیز و گویا می کند.
این رویکرد قدرت روش های LINQ و افزونه را برای تولید کدی کارآمد و قابل نگهداری ترکیب می کند.
هنگام کار با مجموعهها، اغلب نیاز داریم که آیتم خاصی را پیدا کنیم که شرایط خاصی را برآورده کند – مانند یافتن بالاترین، پایینترین یا مناسبترین عنصر بر اساس منطق سفارشی. در حالی که حلقههای سنتی میتوانند چنین مشکلاتی را حل کنند، اغلب پرمخاطب هستند و نگهداری از آنها سختتر است. روش های LINQ و افزونه روشی زیباتر، کارآمدتر و قابل استفاده مجدد را برای حل این مشکلات ارائه می دهند.
در این مقاله، نحوه ساده سازی چنین سناریوهایی را با استفاده از LINQ بررسی خواهیم کرد Aggregate
روش و منطق را در یک روش پسوند قابل استفاده مجدد به نام کپسوله کنید WithMaximum
. ما راه حل را مرحله به مرحله تجزیه می کنیم و تمام جزئیات را توضیح می دهیم تا درک آن آسان شود.
سناریو: انتخاب بهترین کارمند
تصور کنید ما سیستمی داریم که وظایفی را به کارمندان اختصاص می دهد. هر کارمند دارای:
-
Name
: نام آنها -
SkillLevel
: معیاری برای سنجش مهارت آنها. -
IsAvailable
: آیا آنها برای یک کار در دسترس هستند.
هدف این است که یک وظیفه را به ماهرترین کارمند موجود.
مرحله 1: رویکرد پایه LINQ
یک راه حل معمول LINQ ممکن است به شکل زیر باشد:
public Employee GetBestCandidate(IEnumerable<Employee> employees)
{
return employees
.Where(e => e.IsAvailable)
.OrderByDescending(e => e.SkillLevel)
.FirstOrDefault();
}
-
Where
: کارمندان غیرقابل دسترس را فیلتر می کند. -
OrderByDescending
: مرتب سازی بر اساسSkillLevel
به ترتیب نزولی -
FirstOrDefault
: اولین کارمند را از لیست مرتب شده (کسی که بالاترین سطح مهارت را دارد) انتخاب می کند.
این رویکرد کار می کند اما ناکارآمد است زیرا کل مجموعه را مرتب می کند. مرتب سازی دارای پیچیدگی زمانی (O(N \log N)) است که اگر فقط به نتیجه برتر نیاز داشته باشید می تواند غیرضروری باشد.
مرحله 2: راه حل کارآمد با Aggregate
ما می توانیم با اجتناب از مرتب سازی کارایی را بهبود بخشیم. LINQ Aggregate
متد به ما امکان می دهد مجموعه را در یک پاس ((O(N))) اسکن کنیم و آن را برای این کار سریعتر می کند.
public Employee GetBestCandidate(IEnumerable<Employee> employees)
{
return employees
.Where(e => e.IsAvailable)
.Aggregate((currentBest, next) =>
currentBest == null || next.SkillLevel > currentBest.SkillLevel ? next : currentBest);
}
-
Aggregate
:- مجموعه را به یک ارزش واحد کاهش می دهد (ماهرترین کارمند).
- هر کارمند را با هم مقایسه می کند
SkillLevel
به بهترین حالت فعلی
با این حال، Aggregate
می تواند پرمخاطب و خواندن آن سخت تر باشد، به خصوص برای شرایط پیچیده. برای بهبود خوانایی، این منطق را در یک روش پسوند قابل استفاده مجدد قرار می دهیم.
مرحله 3: کپسوله کردن منطق در WithMaximum
این WithMaximum
متد extension به ما اجازه می دهد تا عنصری را با بالاترین مقدار بر اساس یک معیار سفارشی پیدا کنیم. بیایید آن را تجزیه کنیم خط به خط به طور کامل درک کنید که چگونه کار می کند.
کد
public static class EnumerableExtensions
{
public static T WithMaximum<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> selector)
where T : class
where TKey : IComparable<TKey>
{
var tuples = source.Select(item => (Item: item, Key: selector(item)));
var result = tuples.Aggregate(
seed: (Item: null as T, Key: default(TKey)),
(currentBest, next) =>
currentBest.Item == null || next.Key.CompareTo(currentBest.Key) > 0
? next
: currentBest
);
return result.Item;
}
}
تفکیک گام به گام
- اعلامیه روش
public static T WithMaximum<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> selector)
-
روش گسترش:
this
کلمه کلیدی این را به یک روش پسوندی تبدیل می کندIEnumerable
. می توانید آن را مستقیماً در هر دنباله ای فراخوانی کنید (مثلاًemployees.WithMaximum(...)
). -
پارامترهای عمومی:
-
T
: نوع عناصر موجود در مجموعه (به عنوان مثال،Employee
). -
TKey
: نوع مقدار مورد استفاده برای مقایسه (به عنوان مثال،int
برایSkillLevel
).
-
-
Func
:selector - تابعی که یک عنصر از نوع را می گیرد
T
و کلید نوع خود را برمی گرداندTKey
(مثلاe => e.SkillLevel
).
- تابعی که یک عنصر از نوع را می گیرد
- محدودیت های عمومی
where T : class
where TKey : IComparable<TKey>
-
T : class
:- تضمین می کند
T
یک نوع مرجع است، بنابراینnull
می تواند به عنوان دانه اولیه استفاده شود.
- تضمین می کند
-
TKey : IComparable
:- تضمین می کند
TKey
پشتیبانی از مقایسه (CompareTo
) تا بتوانیم حداکثر را تعیین کنیم.
- تضمین می کند
-
کلیدهای پیش محاسبه با
Select
var tuples = source.Select(item => (Item: item, Key: selector(item)));
- هدف: کلید هر عنصر را از قبل محاسبه می کند تا از چندین بار محاسبه مجدد آن جلوگیری کند.
-
نتیجه: هر عنصر را به یک تاپل تبدیل می کند که شامل:
-
Item
: عنصر اصلی. -
Key
: مقدار محاسبه شده (مثلاSkillLevel
).
-
مثال:
اگر source
شامل:
Alice (SkillLevel: 85)
Bob (SkillLevel: 90)
و selector
است e => e.SkillLevel
، سپس tuples
می شود:
[
(Item: Alice, Key: 85),
(Item: Bob, Key: 90)
]
-
تجمیع با
Aggregate
var result = tuples.Aggregate(
seed: (Item: null as T, Key: default(TKey)),
(currentBest, next) =>
currentBest.Item == null || next.Key.CompareTo(currentBest.Key) > 0
? next
: currentBest
);
-
seed
:- ارزش اولیه:
(Item: null, Key: default(TKey))
. -
Item
: شروع به عنوانnull
. -
Key
: به عنوان مقدار پیش فرض شروع می شودTKey
(مثلا0
برایint
).
- ارزش اولیه:
-
منطق:
- مقایسه کنید
next.Key
باcurrentBest.Key
:- اگر
currentBest.Item
استnull
یاnext.Key
بزرگتر است، به روز رسانیcurrentBest
بهnext
. - در غیر این صورت نگه دارید
currentBest
.
- اگر
- مقایسه کنید
مثال:
- تکرار 1:
-
currentBest = (null, 0)
،next = (Alice, 85)
-
currentBest
استnull
، بنابراینcurrentBest = (Alice, 85)
.
-
- تکرار 2:
-
currentBest = (Alice, 85)
،next = (Bob, 90)
-
90 > 85
، بنابراینcurrentBest = (Bob, 90)
.
-
- نتیجه را برگردانید
return result.Item;
- را استخراج کنید
Item
(شیء اصلی) از تاپل نهایی. - در این صورت باز می گردد
Bob
.
مرحله 4: استفاده از WithMaximum
اکنون روش اصلی بسیار ساده تر می شود:
public Employee GetBestCandidate(IEnumerable<Employee> employees)
{
return employees
.Where(e => e.IsAvailable)
.WithMaximum(e => e.SkillLevel);
}
مثال کامل
کلاس کارمند
public class Employee
{
public string Name { get; set; }
public int SkillLevel { get; set; }
public bool IsAvailable { get; set; }
public override string ToString()
{
return $"{Name} (Skill: {SkillLevel}, Available: {IsAvailable})";
}
}
روش گسترش
public static class EnumerableExtensions
{
public static T WithMaximum<T, TKey>(
this IEnumerable<T> source,
Func<T, TKey> selector)
where T : class
where TKey : IComparable<TKey>
{
var tuples = source.Select(item => (Item: item, Key: selector(item)));
var result = tuples.Aggregate(
seed: (Item: null as T, Key: default(TKey)),
(currentBest, next) =>
currentBest.Item == null || next.Key.CompareTo(currentBest.Key) > 0
? next
: currentBest
);
return result.Item;
}
}
برنامه اصلی
class Program
{
static void Main(string[] args)
{
var employees = new List<Employee>
{
new Employee { Name = "Alice", SkillLevel = 85, IsAvailable = true },
new Employee { Name = "Bob", SkillLevel = 90, IsAvailable = false },
new Employee { Name = "Charlie", SkillLevel = 78, IsAvailable = true },
new Employee { Name = "Diana", SkillLevel = 92, IsAvailable = true }
};
var bestCandidate = employees.GetBestCandidate();
Console.WriteLine("Best Candidate:");
Console.WriteLine(bestCandidate
?.ToString() ?? "No available employees.");
}
}
خروجی
Best Candidate:
Diana (Skill: 92, Available: True)
خوراکی های کلیدی
-
کارایی:
WithMaximum
از مرتبسازی غیرضروری اجتناب میکند و مجموعه را در یک پاس اسکن میکند. - قابلیت استفاده مجدد: این روش برای هر نوع منطق جمع آوری و مقایسه قابل استفاده است.
- خوانایی: منطق پیچیده کپسوله شده است و کد مصرف کننده را تمیز و گویا می کند.
این رویکرد قدرت روش های LINQ و افزونه را برای تولید کدی کارآمد و قابل نگهداری ترکیب می کند.