سؤال

دعنا نقول أنني أريد إنشاء فئة تجميع آمنة بشكل افتراضي.

داخليًا ، يحتوي الفصل على محمية List<T> الممتلكات تسمى Values.

بالنسبة للمبتدئين ، من المنطقي أن يكون تطبيق الفصل ICollection<T>. بعض أعضاء هذه الواجهة سهلة التنفيذ ؛ علي سبيل المثال، Count عائدات this.Values.Count.

لكن التنفيذ ICollection<T> يتطلب مني التنفيذ IEnumerable<T> إلى جانب IEnumerable (غير عام) ، وهو أمر صعب بعض الشيء لمجموعة آمنة من الخيوط.

بالطبع ، يمكنني دائمًا رمي ملف NotSupportedException تشغيل IEnumerable<T>.GetEnumerator و IEnumerable.GetEnumerator, ، لكن هذا يبدو وكأنه شرطي بالنسبة لي.

لدي بالفعل مؤشر ترابط getValues الوظيفة التي تغلق Values وإرجاع نسخة في شكل أ T[] مجموعة مصفوفة. لذلك كانت فكرتي في التنفيذ GetEnumerator عن طريق العودة this.getValues().GetEnumerator() بحيث يكون الرمز التالي آمنًا في الواقع:

ThreadSafeCollection coll = new ThreadSafeCollection ();

// add some items to coll

foreach (T value in coll) {
    // do something with value
}

لسوء الحظ ، يبدو أن هذا التنفيذ يعمل من أجله IEnumerable.GetEnumerator, ، وليس الإصدار العام (وبالتالي فإن الكود أعلاه يلقي InvalidCastException).

إحدى الأفكار التي كان لدي ، والتي بدت تعمل ، كانت لإلقاء T[] قيمة الإرجاع من getValues إلى IEnumerable<T> قبل الاتصال GetEnumerator عليه. البديل هو التغيير getValues لإرجاع IEnumerable<T> في المقام الأول ، ولكن بعد ذلك من أجل غير الجيل IEnumerable.GetEnumerator ببساطة يلقي قيمة الإرجاع من getValues إلى غير جنرال IEnumerable. لكن لا يمكنني حقًا أن أقرر ما إذا كانت هذه الأساليب تبدو قذرة أو مقبولة تمامًا.

في أي حال ، هل لدى أي شخص فكرة أفضل عن كيفية القيام بذلك؟ لقد سمعت عن .Synchronized طرق ، ولكن يبدو أنها متاحة فقط للمجموعات غير الجينية في System.Collections مساحة الاسم. ربما هناك متغير عام من هذا موجود بالفعل في .NET لا أعرف عنه ببساطة؟

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

المحلول

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

public IEnumerator<T> GetEnumerator()
{
    lock (this.Values) // or an internal mutex you use for synchronization
    {
        foreach (T val in this.Values)
        {
            yield return val;
        }
    }
    yield break;
}

هذا يفترض أن جميع العمليات الأخرى التي قد تعدل المجموعة قفل أيضًا this.Values. طالما هذا صحيح ، يجب أن تكون بخير.

نصائح أخرى

لسوء الحظ ، يبدو أن هذا التنفيذ يعمل فقط من أجل ienumerable.getenumerator ، وليس الإصدار العام (وبالتالي فإن الكود أعلاه يلقي بمثابة قدر غير صحيح).

يبدو غريبا بالنسبة لي. هل قمت بتنفيذ غير جينسي غير قابلة للتجزئة بشكل صريح؟ أي كتب لك

public IEnumerator<T> GetEnumerator() { ...}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator<T>(); }

أيضًا ، هل حاولت تطبيق ienumerable مع بناء جملة Iderators. يفترض أن تكون سهلة:

public IEnumerator<T> GetEnumerator()
{
    T[] values;
    lock(this.Values)
        values = this.Values.ToArray();
    foreach(var value in values)
        yield return value;
}

إذا حاولت ، فلماذا لا يناسب احتياجاتك؟

نوع T[] لديه واقعية خاصة لهذا النوع System.Array التي تستمد منها. صفائف مثل T[] تم صنعه قبل تقديم الأدوية في .NET (وإلا كان يمكن أن يكون بناء الجملة Array<T>).

نوع System.Array لديه واحد GetEnumerator() طريقة مثيل ، public. هذه الطريقة تُرجع IEnumerator (منذ .net 1). و System.Array لا يوجد لديه تطبيقات واجهة صريحة ل GetEnumerator(). هذا يفسر ملاحظاتك.

ولكن عندما تم تقديم الأدوية الجيرية في .NET 2.0 ، تم اتخاذ "اختراق" خاص ليكون T[] ينفذ IList<T> وواجهاتها الأساسية (التي IEnumerable<T> هو واحد). لهذا السبب أعتقد أنه من المعقول تمامًا استخدام:

((IList<T>)(this.getValues())).GetEnumerator()

في إصدار .NET حيث أتحقق من ذلك ، توجد الفئات التالية:

namespace System
{
  public abstract class Array
  {
    private sealed class SZArrayEnumerator  // instance of this is returned with standard GetEnumerator() on a T[]
    {
    }
  }

  internal sealed class SZArrayHelper
  {
    private sealed class SZGenericArrayEnumerator<T>  // instance of this is returned with generic GetEnumerator() on a T[] which has been cast to IList<T>
    {
    }
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top