سؤال

private IEnumerable<string> Tables
{
    get
    {
        yield return "Foo";
        yield return "Bar";
    }
}

لنفترض أنني أريد تكرار هذه الأشياء وكتابة شيء مثل معالجة #n من #m.

هل هناك طريقة يمكنني من خلالها معرفة قيمة m دون التكرار قبل التكرار الرئيسي؟

آمل أن أكون قد أوضحت نفسي.

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

المحلول

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

إذا كنت تريد أن تعرف عدد العناصر دون بالتكرار عليها يمكنك استخدام ICollection<T>، فإنه له خاصية Count.

نصائح أخرى

وطريقة تمديد System.Linq.Enumerable.Count على IEnumerable<T> لديه التنفيذ التالية:

ICollection<T> c = source as ICollection<TSource>;
if (c != null)
    return c.Count;

int result = 0;
using (IEnumerator<T> enumerator = source.GetEnumerator())
{
    while (enumerator.MoveNext())
        result++;
}
return result;

لذلك يحاول أن يلقي إلى ICollection<T>، الذي له خاصية Count، ويستخدم هذا إن أمكن. وإلا فإنه بالتكرار.

وهكذا أفضل رهان هو استخدام طريقة تمديد Count() على الكائن IEnumerable<T> الخاص بك، كما أنك سوف تحصل على أفضل أداء ممكن بهذه الطريقة.

وفقط مضيفا اضافية بعض المعلومات:

وتمديد Count() لا أعاد دائما. النظر في LINQ إلى SQL، حيث يذهب العد إلى قاعدة البيانات، ولكن بدلا من اعادة كافة الصفوف، فإنه يصدر الأمر Count() SQL والعوائد التي تؤدي بدلا من ذلك.

وبالإضافة إلى ذلك، المترجم (أو وقت التشغيل) ذكي بما فيه الكفاية أنه سيدعو الكائنات Count() طريقة إذا كان لديه واحد. لذلك فمن <م> لا كما يقول المستجيبين أخرى، ويجري جاهل تماما وبالتكرار دائما من أجل الاعتماد العناصر.

في حالات كثيرة لا يكون مبرمج هو مجرد التحقق if( enumerable.Count != 0 ) باستخدام طريقة تمديد Any()، كما هو الحال في if( enumerable.Any() ) هو أكثر كفاءة مع تقييم كسول LINQ كما أنه يمكن قصيرة الدائرة مرة واحدة فإنه يمكن تحديد وجود أي عناصر. كما انها أكثر قابلية للقراءة

لدى أحد أصدقائي سلسلة من منشورات المدونة التي تقدم توضيحًا لسبب عدم قدرتك على القيام بذلك.يقوم بإنشاء دالة تُرجع IEnumerable حيث يُرجع كل تكرار الرقم الأولي التالي، على طول الطريق ulong.MaxValue, ، ولا يتم احتساب العنصر التالي حتى تطلبه.سؤال سريع ومفاجئ:كم عدد العناصر التي تم إرجاعها؟

إليكم المشاركات، لكنها طويلة نوعاً ما:

  1. ما وراء الحلقات (يوفر فئة EnumerableUtility الأولية المستخدمة في المشاركات الأخرى)
  2. تطبيقات التكرار (التنفيذ الأولي)
  3. طرق التمديد المجنونة:ToLazyList (تحسينات الأداء)

وIEnumerable لا يمكن الاعتماد دون بالتكرار.

وتحت ظروف "طبيعية"، سيكون من الممكن لفئات تنفيذ IEnumerable أو IEnumerable ، مثل قائمة ، لتنفيذ أسلوب عدد بإعادة قائمة .Count الممتلكات. ومع ذلك، فإن طريقة العد ليست في الواقع وسيلة المعرفة على IEnumerable أو اجهة IEnumerable. (واحد فقط وهذا هو، في الواقع، هو GetEnumerator.) وهذا يعني أن تطبيق فئة محددة لا يمكن توفيره لذلك.

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

وبدلا من ذلك يمكنك القيام بما يلي:

Tables.ToList<string>().Count;

لا، ليس بشكل عام. نقطة واحدة في استخدام enumerables هي أن مجموعة الفعلي من الكائنات في التعداد غير معروف (مقدما، أو حتى على الإطلاق).

ويمكنك استخدام System.Linq.

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    private IEnumerable<string> Tables
    {
        get {
             yield return "Foo";
             yield return "Bar";
         }
    }

    static void Main()
    {
        var x = new Test();
        Console.WriteLine(x.Tables.Count());
    }
}

وستحصل على نتيجة "2".

والذهاب أبعد من السؤال المباشر الخاص بك (والتي تم الإجابة بدقة في السلبية)، إذا كنت تبحث الإبلاغ عن التقدم المحرز في حين تجهيز وenumerable، قد ترغب في النظر في بلدي بلوق وظيفة <لأ href = "HTTP: // blog.functionalfun.net/2008/07/reporting-progress-during-linq-queries.html "يختلط =" noreferrer "> الإبلاغ عن التقدم المحرز خلال ينق استعلامات .

وهذا يتيح لك القيام بذلك:

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (sender, e) =>
      {
          // pretend we have a collection of 
          // items to process
          var items = 1.To(1000);
          items
              .WithProgressReporting(progress => worker.ReportProgress(progress))
              .ForEach(item => Thread.Sleep(10)); // simulate some real work
      };

ولقد استخدمت هذه الطريقة داخل وسيلة للتحقق من مر في محتوى IEnumberable

if( iEnum.Cast<Object>().Count() > 0) 
{

}

وداخل طريقة مثل هذا:

GetDataTable(IEnumberable iEnum)
{  
    if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further

}

وهذا يعتمد على إصدار صافي وتنفيذ الكائن IEnumerable الخاص بك. مايكروسوفت قد حددت طريقة IEnumerable.Count للتحقق من تنفيذ، ويستخدم ICollection.Count أو ICollection .Count، انظر التفاصيل هنا <لأ href = "https://connect.microsoft.com/VisualStudio/feedback/ تفاصيل / 454130 "يختلط =" نوفولو "> https://connect.microsoft.com/VisualStudio/feedback/details/454130

وأدناه هو MSIL من ILDASM لSystem.Core، الذي يقيم System.Linq.

.method public hidebysig static int32  Count<TSource>(class 

[mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource> source) cil managed
{
  .custom instance void System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       85 (0x55)
  .maxstack  2
  .locals init (class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource> V_0,
           class [mscorlib]System.Collections.ICollection V_1,
           int32 V_2,
           class [mscorlib]System.Collections.Generic.IEnumerator`1<!!TSource> V_3)
  IL_0000:  ldarg.0
  IL_0001:  brtrue.s   IL_000e
  IL_0003:  ldstr      "source"
  IL_0008:  call       class [mscorlib]System.Exception System.Linq.Error::ArgumentNull(string)
  IL_000d:  throw
  IL_000e:  ldarg.0
  IL_000f:  isinst     class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>
  IL_0014:  stloc.0
  IL_0015:  ldloc.0
  IL_0016:  brfalse.s  IL_001f
  IL_0018:  ldloc.0
  IL_0019:  callvirt   instance int32 class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>::get_Count()
  IL_001e:  ret
  IL_001f:  ldarg.0
  IL_0020:  isinst     [mscorlib]System.Collections.ICollection
  IL_0025:  stloc.1
  IL_0026:  ldloc.1
  IL_0027:  brfalse.s  IL_0030
  IL_0029:  ldloc.1
  IL_002a:  callvirt   instance int32 [mscorlib]System.Collections.ICollection::get_Count()
  IL_002f:  ret
  IL_0030:  ldc.i4.0
  IL_0031:  stloc.2
  IL_0032:  ldarg.0
  IL_0033:  callvirt   instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource>::GetEnumerator()
  IL_0038:  stloc.3
  .try
  {
    IL_0039:  br.s       IL_003f
    IL_003b:  ldloc.2
    IL_003c:  ldc.i4.1
    IL_003d:  add.ovf
    IL_003e:  stloc.2
    IL_003f:  ldloc.3
    IL_0040:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    IL_0045:  brtrue.s   IL_003b
    IL_0047:  leave.s    IL_0053
  }  // end .try
  finally
  {
    IL_0049:  ldloc.3
    IL_004a:  brfalse.s  IL_0052
    IL_004c:  ldloc.3
    IL_004d:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0052:  endfinally
  }  // end handler
  IL_0053:  ldloc.2
  IL_0054:  ret
} // end of method Enumerable::Count

وهنا لمناقشة كبيرة حول <لأ href = "http://blogs.msdn.com/ericwhite/pages/Lazy-Evaluation-_2800_and-in-contrast_2C00_-Eager-Evaluation_2900_.aspx" يختلط = "نوفولو noreferrer" > تقييم كسول و المؤجلة التنفيذ . أساسا لديك لتجسيد القائمة للحصول على تلك القيمة.

والنتيجة من وظيفة IEnumerable.Count () قد تكون خاطئة. هذه عينة بسيطة جدا لاختبار:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      var test = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
      var result = test.Split(7);
      int cnt = 0;

      foreach (IEnumerable<int> chunk in result)
      {
        cnt = chunk.Count();
        Console.WriteLine(cnt);
      }
      cnt = result.Count();
      Console.WriteLine(cnt);
      Console.ReadLine();
    }
  }

  static class LinqExt
  {
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkLength)
    {
      if (chunkLength <= 0)
        throw new ArgumentOutOfRangeException("chunkLength", "chunkLength must be greater than 0");

      IEnumerable<T> result = null;
      using (IEnumerator<T> enumerator = source.GetEnumerator())
      {
        while (enumerator.MoveNext())
        {
          result = GetChunk(enumerator, chunkLength);
          yield return result;
        }
      }
    }

    static IEnumerable<T> GetChunk<T>(IEnumerator<T> source, int chunkLength)
    {
      int x = chunkLength;
      do
        yield return source.Current;
      while (--x > 0 && source.MoveNext());
    }
  }
}

ويجب أن يكون نتيجة (7،7،3،3) ولكن النتيجة الفعلية هي (7،7،3،17)

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


    public interface IEnumAndCount<out T> : IEnumerable<T>
    {
        int Count { get; }
    }

إذا لم يكن لديك مجموعة الأصلي أي مفهرس، يمكن تنفيذ عدد بك أعاد خلال جمع، مع ضرب المعروفة في O الأداء (ن).

إذا كنت لا تريد استخدام شيء مشابه لIEnumAndCount، وأفضل رهان هو أن يذهب مع Linq.Count عن الأسباب التي دانيال Earwicker بالقرب من أعلى هذه المسألة.

وحظا سعيدا!

لا.

هل ترى أن المعلومات المتاحة في أي مكان في رمز كنت قد كتبت؟

هل يمكن ان نقول ان المجمع يمكن "رؤية" أن هناك اثنين فقط، ولكن هذا يعني أنه سيحتاج إلى تحليل كل أسلوب مكرر تبحث فقط لهذه الحالة المرضية الخاصة. وحتى لو فعلت، كيف تقرأ ذلك، نظرا للحدود من IEnumerable؟

وأود أن أقترح الدعوة ToList. نعم تقومون به التعداد في وقت مبكر، ولكن لا يزال لديك الوصول إلى قائمة العناصر.

قد لا تسفر عن أفضل أداء، ولكن يمكنك استخدام LINQ لحساب العناصر في IEnumerable:

public int GetEnumerableCount(IEnumerable Enumerable)
{
    return (from object Item in Enumerable
            select Item).Count();
}

وأنا استخدم IEnum<string>.ToArray<string>().Length وأنه يعمل بشكل جيد.

وأنا استخدم هذا الرمز، إذا كان لدي قائمة سلاسل:

((IList<string>)Table).Count
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top