هل هو موافق لاستخدام كثافة العمليات للمفتاح في KeyedCollection

في كثير من الأحيان أحتاج إلى مجموعة من الأشياء غير المتسلسلة ذات المعرّفات الرقمية. أحب استخدام KeyedCollection لهذا ، ولكن أعتقد أن هناك عيبًا خطيرًا. إذا كنت تستخدم int int للمفتاح ، فلن تتمكن بعد ذلك من الوصول إلى أعضاء المجموعة عن طريق فهرسهم (المجموعة [الفهرس] هي الآن مجموعة حقا [مفتاح]). هل هذه مشكلة خطيرة بما يكفي لتجنب استخدام int كمفتاح؟ ما هو البديل المفضل ليكون؟ (ربما int.ToString ()؟)

لقد فعلت ذلك من قبل دون أي مشاكل كبيرة ، لكنني في الآونة الأخيرة أصبت بعقبة سيئة حيث يعمل تنسيق xml مع KeyedCollection على لا إذا كان المفتاح هو int ، بسبب خطأ في .NET .

11

4 إجابة

تحتاج في الأساس إلى تحديد ما إذا كان من المحتمل أن يتم خلط مستخدمي الفصل بسبب عدم تمكنهم ، على سبيل المثال ، من القيام بما يلي:

for(int i=0; i=< myCollection.Count; i++)
{
    ... myCollection[i] ...
}

على الرغم من أنه يمكنهم بالطبع استخدام foreach أو استخدام طاقم Cast:

for(int i=0; i=< myCollection.Count; i++)
{
    ... ((Collection)myCollection)[i] ...
}

إنه ليس قرارًا سهلاً ، لأنه يمكن أن يؤدي بسهولة إلى heisenbugs. قررت السماح بذلك في أحد تطبيقاتي ، حيث كان الوصول من مستخدمي الفصل تقريبًا من خلال المفتاح.

I'm not sure I'd do so for a shared class library though: in general I'd avoid exposing a KeyedCollection in a public API: instead I would expose IList in a public API, and consumers of the API who need keyed access can define their own internal KeyedCollection with a constructor that takes an IEnumerable and populates the collection with it. This means you can easily build a new KeyedCollection from a list retrieved from an API.

فيما يتعلق بالتسلسل ، هناك أيضًا مشكلة في الأداء التي أبلغت بها لـ Microsoft Connect : يحتفظ KeyedCollection بقاموس داخلي بالإضافة إلى قائمة ، ويقوم بتسلسل كلاهما - يكفي إجراء تسلسل للقائمة كما يمكن بسهولة إعادة إنشاء القاموس عند إلغاء التسلسل.

لهذا السبب ، بالإضافة إلى خطأ XmlSerialization ، نوصيك بتجنب تسلسل KeyedCollection - بدلاً من ذلك فقط قم بتسلسل قائمة KeyedCollection.Items.

لا أحب اقتراح التفاف مفتاح int الخاص بك في نوع آخر . يبدو لي أنه من الخطأ إضافة التعقيد ببساطة حتى يمكن استخدام نوع ما كعنصر في KeyedCollection. فما استقاموا لكم فاستقيموا استخدام مفتاح سلسلة (ToString) بدلا من القيام بذلك - هذا هو مثل فئة جمع VB6.

لقد سألتُ FWIW عن نفس السؤال منذ بعض الوقت على منتديات MSDN. هناك رد من أحد أعضاء فريق FxCop ، ولكن ليس هناك إرشادات قاطعة.

7
وأضاف
يمكنك تعطيل القاموس عبر الحد الأدنى = -1
وأضاف المؤلف paparazzo, مصدر
أعتقد أنني أتفق مع عدم تعريض KeyedCollection الذي يستخدم int للحصول على المفتاح في API. هذا من المحتمل أن يؤدي إلى أخطاء على الطرف الآخر. قضايا التسلسل هي بالتأكيد سبب وجيه لعدم تسلسل المجموعة نفسها ، ولكن فقط العناصر (وهو ما فعلته في حالتي).
وأضاف المؤلف Jon B, مصدر
هناك أيضًا أسلوب استخدام فئة مشتقة من KeyedCollection خصيصًا للمفاتيح الصحيحة التي تتضمن أسلوب GetByIndex أو ItemAt الذي يستخدم مجموعة العناصر الداخلية للحصول على العنصر المناسب. يتيح لك هذا الاستفادة من KeyedCollection الموجودة ، بدلاً من النهج أدناه الذي يتضمن زيادة مجموعة المختارات.
وأضاف المؤلف Thomas S. Trias, مصدر

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

struct Id {
    public int Value;

    public Id(int value) { Value = value; }

    override int GetHashCode() { return Value.GetHashCode(); }

   //… Equals method.
}
3
وأضاف
هذه فكرة جيدة. ومع ذلك ، فمن المستحسن دائمًا جعل البنى غير قابلة للتغيير. هذا يتجنب الارتباك مثل ما يلي: معرف معرف؛ id.Value = 2 ؛ معرف id2 = id؛ id2.Value = 4؛//ما هو id.Value بعد هذا؟ يتوقع الكثيرون أن يكون الرقم 4 (آسف للتنسيق ، يمكنني استخدام فواصل الأسطر إذا أمكن)
وأضاف المؤلف Neil, مصدر

It might be best to add a GetById(int) method to a collection type. Collection can be used instead if you don't need any other key for accessing the contained objects:

public class FooCollection : Collection
 { Dictionary dict = new Dictionary();

   public Foo GetById(int id) { return dict[id]; }

   public bool Contains(int id) { return  dict.Containskey(id);}

   protected override void InsertItem(Foo f)
    { dict[f.Id] = f;
      base.InsertItem(f);
    }

   protected override void ClearItems()
    { dict.Clear();
      base.ClearItems();
    }

   protected override void RemoveItem(int index)
    { dict.Remove(base.Items[index].Id);
      base.RemoveItem(index);
    }

   protected override void SetItem(int index, Foo item)
    { dict.Remove(base.Items[index].Id);
      dict[item.Id] = item;
      base.SetItem(index, item);
    }
 }









 }
2
وأضاف
هذا هو أفضل إجابة إيمو
وأضاف المؤلف nawfal, مصدر

يجب أن يكون المفتاح في KeyedCollection فريدًا ويمكن اشتقاقه بسرعة من الكائن الذي يتم جمعه. إذا حصلت على فئة شخص ، على سبيل المثال ، يمكن أن تكون خاصية SSN أو ربما تساوي خصائص FirstName و LastName (إذا كانت النتيجة معروفة بأنها فريدة). إذا كان المعرف بشكل قانوني هو حقل الكائن الذي يتم جمعه من كونه مرشحًا صالحًا للمفتاح. ولكن ربما حاول وضعه كسلسلة بدلاً من تجنب الاصطدام.

1
وأضاف