[C#] IEnumerable مقابل قائمة - ما لاستخدام؟ كيف يعملون؟



Answers

الفئة التي تقوم بتطبيق IEnumerable تسمح لك باستخدام بناء جملة foreach .

أساسا لديه طريقة للحصول على العنصر التالي في المجموعة. لا تحتاج المجموعة كلها إلى أن تكون في الذاكرة ولا تعرف عدد العناصر الموجودة بها ، فقط foreach تستمر في الحصول على العنصر التالي حتى تنفد.

قد يكون هذا مفيدًا جدًا في ظروف معينة ، على سبيل المثال في جدول قاعدة بيانات ضخمة لا تريد نسخ كل شيء في الذاكرة قبل بدء معالجة الصفوف.

الآن List تنفذ IEnumerable ، ولكن يمثل المجموعة بأكملها في الذاكرة. إذا كان لديك IEnumerable وقمت باستدعاء .ToList() يمكنك إنشاء قائمة جديدة بمحتويات التعداد في الذاكرة.

تعيد التعبير linq تعدادًا ، ويتم تنفيذ التعبير بشكل افتراضي عند التكرار من خلال استخدام foreach . ينفّذ بيان linq IEnumerable عند تكرار foreach ولكن يمكنك فرض ذلك إلى التكرار استخدام أقرب .ToList() .

إليك ما أعنيه:

var things = 
    from item in BigDatabaseCall()
    where ....
    select item;

// this will iterate through the entire linq statement:
int count = things.Count();

// this will stop after iterating the first one, but will execute the linq again
bool hasAnyRecs = things.Any();

// this will execute the linq statement *again*
foreach( var thing in things ) ...

// this will copy the results to a list in memory
var list = things.ToList()

// this won't iterate through again, the list knows how many items are in it
int count2 = list.Count();

// this won't execute the linq statement - we have it copied to the list
foreach( var thing in list ) ...
Question

لدي بعض الشكوك حول كيفية عمل الباحثين ، و LINQ. خذ بعين الاعتبار هذين الاختيارين البسيطين:

List<Animal> sel = (from animal in Animals 
                    join race in Species
                    on animal.SpeciesKey equals race.SpeciesKey
                    select animal).Distinct().ToList();

أو

IEnumerable<Animal> sel = (from animal in Animals 
                           join race in Species
                           on animal.SpeciesKey equals race.SpeciesKey
                           select animal).Distinct();

لقد غيرت أسماء الكائنات الأصلية بحيث يبدو هذا كمثال أكثر عمومية. الاستعلام نفسه ليس بتلك الأهمية. ما أريد أن أسأله هو:

foreach (Animal animal in sel) { /*do stuff*/ }
  1. لقد لاحظت أنه في حالة استخدام IEnumerable ، عندما أقوم بالتصحيح وفحص "sel" ، والذي في هذه الحالة هو IEnumerable ، فإنه يحتوي على بعض أعضاء مثيرة للاهتمام: "الداخلي" ، "الخارجي" ، "innerKeySelector" و "outerKeySelector" ، هذه الأخيرة 2 يبدو أن المندوبين. ليس لدى العضو "الداخلي" مثيلات "الحيوان" في ذلك ، ولكن بالأحرى مثيلات "الأنواع" ، التي كانت غريبة جداً بالنسبة لي. يحتوي العضو "الخارجي" على مثيلات "Animal". أفترض أن اثنين من المندوبين تحديد الذي يدخل وما يخرج منه؟

  2. لاحظت أنه إذا كنت تستخدم "متميز" ، فإن "الداخلي" يحتوي على 6 عناصر (وهذا غير صحيح لأن 2 فقط هي مميزة) ، ولكن "الخارجية" تحتوي على القيم الصحيحة. مرة أخرى ، ربما تحدد الأساليب المفوضة هذا ولكن هذا أكثر قليلاً مما أعرفه عن IEnumerable.

  3. الأهم من ذلك ، أي من الخيارين هو أفضل أداء؟

تحويل قائمة الشر عبر .ToList() ؟

أو ربما استخدام العداد مباشرة؟

إذا كنت تستطيع ، يرجى أيضا شرح بعض الشيء أو رمي بعض الروابط التي توضح هذا الاستخدام من IEnumerable.




أهم شيء هو إدراك أنه ، باستخدام Linq ، لا يتم تقييم الاستعلام على الفور. يتم تشغيله فقط كجزء من التكرار خلال IEnumerable<T> الناتجة في foreach - هذا ما يفعله جميع المندوبين الغريبين.

لذلك ، يقوم المثال الأول بتقييم الاستعلام على الفور من خلال استدعاء ToList ووضع نتائج الاستعلام في قائمة.
المثال الثاني بإرجاع IEnumerable<T> يحتوي على كافة المعلومات المطلوبة لتشغيل الاستعلام فيما بعد.

من حيث الأداء ، فإن الجواب يعتمد . إذا كنت بحاجة إلى تقييم النتائج في وقت واحد (على سبيل المثال ، أنت تقوم بتحوير البنى التي تستعلم عنها لاحقًا ، أو إذا كنت لا تريد التكرار على مدة أطول من IEnumerable<T> لكي تستخدم وقتًا طويلاً) استخدم قائمة . آخر استخدام IEnumerable<T> . يجب أن يكون الإعداد الافتراضي هو استخدام التقييم عند الطلب في المثال الثاني ، حيث أنه يستخدم ذاكرة أقل بشكل عام ، ما لم يكن هناك سبب محدد لتخزين النتائج في قائمة.




سوف أشارك أحد المفاهيم التي أساء استخدامها أنني وقعت في يوم واحد:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));


// updating existing list
names[0] = "ford";

// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

نتيجة متوقعة

// I was expecting    
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari

نتيجة فعلية

// what printed actualy   
print( startingWith_M.ToList() ); // mercedes
print( startingWith_F.ToList() ); // ford, fiat, ferrari

تفسير

وفقا لإجابات أخرى ، تم تأجيل تقييم النتيجة حتى استدعاء ToList أو أساليب استدعاء مشابهة على سبيل المثال ToArray .

حتى أتمكن من إعادة كتابة الشفرة في هذه الحالة على النحو التالي:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list
names[0] = "ford";

// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

لعب حولها

https://repl.it/E8Ki/0




تتمثل ميزة IEnumerable في التنفيذ المؤجل (عادةً مع قواعد البيانات). لن يتم تنفيذ الاستعلام حتى تتتكرر خلال البيانات. إنه استعلام ينتظر حتى الحاجة إليه (ويعرف أيضًا باسم التحميل البطيء).

إذا قمت بالاتصال ToList ، سيتم تنفيذ الاستعلام ، أو "تتحقق" كما أحب أن أقول.

الاثنين لهم ايجابيات وسلبيات. إذا قمت بالاتصال بـ ToList ، فيمكنك إزالة بعض الغموض حول وقت تنفيذ الاستعلام. إذا كنت تلتزم بـ IEnumerable ، فستحصل على ميزة أن البرنامج لا يقوم بأي عمل حتى يكون مطلوبًا بالفعل.




Links