ما هي أفضل طريقة للتكرار من خلال قائمة عامة مكتوبة بقوة<T>؟

StackOverflow https://stackoverflow.com/questions/15204

سؤال

ما هي أفضل طريقة للتكرار من خلال قائمة عامة مكتوبة بقوة في C#.NET وVB.NET؟

هل كانت مفيدة؟

المحلول

بالنسبة لـ C#:

foreach(ObjectType objectItem in objectTypeList)
{
    // ...do some stuff
}

الإجابة على VB.NET من النملة الأرجوانية:

For Each objectItem as ObjectType in objectTypeList
    'Do some stuff '
Next

نصائح أخرى

مع أي تطبيق عام لـ IEnumerable فإن أفضل طريقة هي:

//C#
foreach( var item in listVariable) {
    //do stuff
}

ولكن هناك استثناء مهم.يتضمن IEnumerable حملًا إضافيًا لـ Current() وMoveNext() وهو ما يتم تجميع حلقة foreach فيه بالفعل.

عندما يكون لديك مجموعة بسيطة من الهياكل:

//C#
int[] valueTypeArray;
for(int i=0; i < valueTypeArray.Length; ++i) {
     int item = valueTypeArray[i];
     //do stuff
}

أسرع.


تحديث

بعد مناقشة معSteven Sudit (انظر التعليقات) أعتقد أن نصيحتي الأصلية قد تكون قديمة أو خاطئة، لذلك أجريت بعض الاختبارات:

// create a list to test with
var theList = Enumerable.Range(0, 100000000).ToList();

// time foreach
var sw = Stopwatch.StartNew();
foreach (var item in theList)
{
    int inLoop = item;
}
Console.WriteLine("list  foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
int cnt = theList.Count;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theList[i];
}
Console.WriteLine("list  for    : " + sw.Elapsed.ToString());

// now run the same tests, but with an array
var theArray = theList.ToArray();

sw.Reset();
sw.Start();

foreach (var item in theArray)
{
    int inLoop = item;
}
Console.WriteLine("array foreach: " + sw.Elapsed.ToString());

sw.Reset();
sw.Start();

// time for
cnt = theArray.Length;
for (int i = 0; i < cnt; i++)
{
    int inLoop = theArray[i];
}
Console.WriteLine("array for    : " + sw.Elapsed.ToString());

Console.ReadKey();

لذلك، قمت بتشغيل هذا الإصدار مع جميع التحسينات:

list  foreach: 00:00:00.5137506
list  for    : 00:00:00.2417709
array foreach: 00:00:00.1085653
array for    : 00:00:00.0954890

ثم قم بالتصحيح بدون تحسينات:

list  foreach: 00:00:01.1289015
list  for    : 00:00:00.9945345
array foreach: 00:00:00.6405422
array for    : 00:00:00.4913245

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

ومع ذلك، يتم ذلك عبر 100,000,000 تكرار، ويبلغ الفرق حوالي 0.4 من الثانية بين أسرع الطرق وأبطأها.ما لم تكن تقوم بحلقات ضخمة للأداء، فلا داعي للقلق بشأنها.

بالنسبة لـ VB.NET:

For Each tmpObject as ObjectType in ObjectTypeList
    'Do some stuff '
Next

ج#

myList<string>().ForEach(
    delegate(string name)
    {
        Console.WriteLine(name);
    });

لا يتم حاليًا تنفيذ المفوضين المجهولين في VB.Net، ولكن يجب أن يكون كل من C# وVB.Net قادرين على تنفيذ عمليات lambda:

ج#

myList<string>().ForEach(name => Console.WriteLine(name));

VB.Net

myList(Of String)().ForEach(Function(name) Console.WriteLine(name))

كما أشار Grauenwolf إلى أن VB أعلاه لن يتم تجميعه نظرًا لأن lambda لا يُرجع قيمة.من المحتمل أن تكون حلقة ForEach العادية، كما اقترح الآخرون، هي الأسهل في الوقت الحالي، ولكن كالمعتاد، يتطلب الأمر كتلة من التعليمات البرمجية للقيام بما يمكن أن تفعله C# في سطر واحد.


فيما يلي مثال مبتذل على سبب كون ذلك مفيدًا:يمنحك هذا القدرة على تمرير منطق الحلقة من نطاق آخر غير المكان الذي يوجد فيه IEnumerable، لذلك لا يتعين عليك حتى كشفه إذا كنت لا تريد ذلك.

لنفترض أن لديك قائمة بمسارات عناوين URL النسبية التي تريد جعلها مطلقة:

public IEnumerable<String> Paths(Func<String> formatter) {
    List<String> paths = new List<String>()
    {
        "/about", "/contact", "/services"
    };

    return paths.ForEach(formatter);
}

إذن يمكنك استدعاء الوظيفة بهذه الطريقة:

var hostname = "myhost.com";
var formatter = f => String.Format("http://{0}{1}", hostname, f);
IEnumerable<String> absolutePaths = Paths(formatter);

يعطى لك "http://myhost.com/about", "http://myhost.com/contact" إلخ.من الواضح أن هناك طرقًا أفضل لتحقيق ذلك في هذا المثال المحدد، فأنا أحاول فقط توضيح المبدأ الأساسي.

بدون معرفة التنفيذ الداخلي للقائمة، أعتقد بشكل عام أن أفضل طريقة للتكرار عليها هي حلقة foreach.نظرًا لأن foreach يستخدم IEnumerator للتنقل عبر القائمة، فإن الأمر متروك للقائمة نفسها لتحديد كيفية الانتقال من كائن إلى آخر.

إذا كان التنفيذ الداخلي، على سبيل المثال، عبارة عن قائمة مرتبطة، فستكون حلقة for البسيطة أبطأ قليلاً من foreach.

هل هذا منطقي؟

ذلك يعتمد على التطبيق الخاص بك:

  • للحلقة، إذا كانت الكفاءة هي الأولوية
  • حلقة foreach أو طريقة ForEach، أيهما ينقل نيتك بشكل أكثر وضوحًا

ربما أفتقد شيئًا ما، ولكن التكرار عبر قائمة عامة يجب أن يكون بسيطًا إلى حد ما إذا كنت تستخدم الأمثلة أدناه.تطبق فئة List<> واجهات IList وIEnumerable بحيث يمكنك التكرار من خلالها بسهولة بأي طريقة تريدها.

الطريقة الأكثر فعالية هي استخدام حلقة for:

for(int i = 0; i < genericList.Count; ++i) 
{
     // Loop body
}

يمكنك أيضًا اختيار استخدام حلقة foreach:

foreach(<insertTypeHere> o in genericList)
{
    // Loop body
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top