برنامه نویسی

ساده سازی انتخاب های پیچیده با روش های 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;
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


تفکیک گام به گام

  1. اعلامیه روش
   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).

  1. محدودیت های عمومی
   where T : class
   where TKey : IComparable<TKey>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • T : class:

    • تضمین می کند T یک نوع مرجع است، بنابراین null می تواند به عنوان دانه اولیه استفاده شود.
  • TKey : IComparable:

    • تضمین می کند TKey پشتیبانی از مقایسه (CompareTo) تا بتوانیم حداکثر را تعیین کنیم.

  1. کلیدهای پیش محاسبه با 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)
   ]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


  1. تجمیع با 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).

  1. نتیجه را برگردانید
   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)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید


خوراکی های کلیدی

  1. کارایی: WithMaximum از مرتب‌سازی غیرضروری اجتناب می‌کند و مجموعه را در یک پاس اسکن می‌کند.
  2. قابلیت استفاده مجدد: این روش برای هر نوع منطق جمع آوری و مقایسه قابل استفاده است.
  3. خوانایی: منطق پیچیده کپسوله شده است و کد مصرف کننده را تمیز و گویا می کند.

این رویکرد قدرت روش های LINQ و افزونه را برای تولید کدی کارآمد و قابل نگهداری ترکیب می کند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا