هل من الشائع (أو المشجع) ممارسة التحميل الزائد لقبول ، iCollection ، iList ، إلخ؟

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

سؤال

تعديل:

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


لنفترض أن لدي ثلاثة أحمال زائدة من الوظيفة ، واحد يأخذ IEnumerable<T>, ، واحد يأخذ ICollection<T>, وأخذ واحد IList<T>, ، شيء مثل ما يلي:

public static T GetMiddle<T>(IEnumerable<T> values) {
    IList<T> list = values as IList<T>;
    if (list != null) return GetMiddle(list);

    int count = GetCount<T>(values);

    T middle = default(T);
    int index = 0;

    foreach (T value in values) {
        if (index++ >= count / 2) {
            middle = value;
            break;
        }
    }

    return middle;
}

private static T GetMiddle<T>(IList<T> values) {
    int middleIndex = values.Count / 2;
    return values[middleIndex];
}

private static int GetCount<T>(IEnumerable<T> values) {
    // if values is actually an ICollection<T> (e.g., List<T>),
    // we can get the count quite cheaply
    ICollection<T> genericCollection = values as ICollection<T>;
    if (genericCollection != null) return genericCollection.Count;

    // same for ICollection (e.g., Queue<T>, Stack<T>)
    ICollection collection = values as ICollection;
    if (collection != null) return collection.Count;

    // otherwise, we've got to count values ourselves
    int count = 0;
    foreach (T value in values) count++;

    return count;
}

الفكرة هنا هي أنه إذا كان لدي IList<T>, هذا يجعل وظيفتي أسهل ؛ من ناحية أخرى ، لا يزال بإمكاني القيام بالمهمة باستخدام ICollection<T> أو حتى IEnumerable<T>; ؛ تطبيق تلك الواجهات ليس فعالًا.

لم أكن متأكدًا مما إذا كان هذا سيعمل (إذا كان وقت التشغيل سيكون قادرًا على اختيار التحميل الزائد بناءً على المعلمة التي تم تمريرها) ، لكنني اختبرته ويبدو أنه.

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

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

المحلول

إذا ألقيت نظرة على كيفية تنفيذ طرق تمديد LINQ باستخدام Reflector ، فيمكنك أن ترى أن بعض طرق التمديد على iEnumerableu003CT> ، مثل COUNT () ، محاولة إلقاء التسلسل إلى iCollectionu003CT> أو ilistu003CT> لتحسين العملية (على سبيل المثال ، باستخدام iCollectionu003CT> .u003CT> وحساب العناصر). لذا فإن أفضل رهان لك هو على الأرجح أن تقبل أي شيء لا يطاقu003CT> ثم قم بهذا النوع من التحسينات إذاu003CT> أو ilistu003CT> تتوفر.

نصائح أخرى

أعتقد أن نسخة واحدة تقبل iEnumerableu003CT> سيكون الطريق للذهاب ، والتحقق من الطريقة إذا كانت المعلمة واحدة من أنواع التجميع الأكثر اشتقاقًا. مع ثلاثة إصدارات كما تقترح ، تفقد فائدة الكفاءة إذا نجح شخص ماu003CT> أن المترجم ينظر بشكل ثابتu003CT> :

        IList<string> stringList = new List<string> { "A", "B", "C" };
        IEnumerable<string> seq = stringList;
        Extensions.GetMiddle(stringList); // calls IList version
        Extensions.GetMiddle(seq);        // calls IEnumerable version

أود أن أقول إنه من غير المألوف ، وربما مربكًا ، فمن غير المرجح أن يكون اختيارًا جيدًا لواجهة برمجة التطبيقات العامة.

يمكن أن تقبل iEnumerableu003CT> المعلمة ، وتحقق داخليًا ما إذا كان في الواقع عبارة عن iCollectionu003CT> أو ilistu003CT> ، وتحسين وفقا لذلك.

قد يكون هذا تشابهًا لبعض التحسينات في بعض من غيرهاu003CT> طرق التمديد في إطار .NET 3.5.

أنا حقا غير مبال. إذا رأيت ذلك طريقك ، فلن أفكر في أي شيء. لكن فكرة جو لها ميزة. قد يبدو كما يلي.

public static T GetMiddle<T>(IEnumerable<T> values)
{
  if (values is IList<T>) return GetMiddle((IList<T>)values);
  if (values is ICollection<T>) return GetMiddle((ICollection<T>)values);

  // Use the default implementation here.
}

private static T GetMiddle<T>(ICollection<T> values)
{
}

private static T GetMiddle<T>(IList<T> values)
{
}

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

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

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

إذا كنت تستطيع القيام بذلك مع IEnumerable<T> ثم استخدم ذلك ، إن لم يكن ، استخدم أقل واجهة مطلوبة.

لا. إنه بالتأكيد غير شائع.

على أي حال. منذ ilistu003CT> وراثة من icollectionu003CT> و ienumerableu003CT> و iCollectionu003CT> يرث من ienumerableu003CT> ، سيكون اهتمامك الوحيد هو الأداء في iEnumerableu003CT> الأنواع.

لا أرى أي سبب لإفراط في تحميل الوظيفة بهذه الطريقة ، وتوفير توقيعات مختلفة لتحقيق نفس النتيجة تمامًا وقبول نفس الأنواع تمامًا مثل المعلمة (بغض النظر عما إذا كان لديك ienumerformu003CT> أو ilistu003CT> ، ستكون قادرًا على تمريره إلى أي من الأحمال الزائدة الثلاثة) ؛ من شأنه أن يسبب الالتباس.

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top