سؤال

لدي فئة أساسية:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

وصنفين مشتقين

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

حسنًا، لنفترض الآن أن لدي قائمة بالعناصر:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

وأريدهم جميعًا أن يتصلوا بأسلوب DoSomething () الخاص بهم.يمكنني أن أتوقع مجرد تكرار القائمة واستدعاء طريقة DoSomething() الخاصة بهم، لذلك لنفترض أن لدي طريقة للقيام بذلك تسمى AllDoSomething() والتي تتكرر فقط عبر القائمة وتؤدي المهمة:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

ما هو الفرق العملي للطريقة التالية؟

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

تظهر كلا الطريقتين من الناحية الحقيقية، على الرغم من اختلافهما من الناحية النحوية، إلا أنهما تفعلان الشيء نفسه.

هل هي مجرد طرق مختلفة لفعل نفس الشيء؟أنا أفهم الأدوية العامة وقيود الكتابة ولكن لا أستطيع أن أرى سبب استخدام طريقة واحدة بدلاً من الأخرى في هذه الحالة.

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

المحلول

هذا لأنه حتى الآن لا يدعم لغة C# التغاير.

بشكل أكثر رسمية ، في C# V2.0 إذا كان T نوعًا فرعيًا لـ U ، فإن T [] هو نوع فرعي لـ U [] ، لكن G ليس نوعًا فرعيًا من G (حيث يكون G أي نوع عام).في المصطلحات النظرية ، وصفنا هذا السلوك بالقول إن أنواع الصفيف C# هي "متغيرة" وأن الأنواع العامة "ثابتة".

مرجع: http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

إذا كان لديك الطريقة التالية:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile

حيث كما لو كنت تستخدم قيد النوع العام، فسيحدث ذلك.

لمزيد من المعلومات حول التباين والتباين]، راجع ذلك سلسلة منشورات إريك ليبرت.


منشورات أخرى تستحق القراءة:

نصائح أخرى

لنفترض كان لديك القائمة:

List<Stuff1> l = // get from somewhere

والآن محاولة:

AllDoSomething(l);

مع إصدار عام، سوف يسمح ذلك. مع غير عام، وسوف لا. هذا هو الفرق الجوهري. قائمة Stuff1 ليست قائمة StuffBase. ولكن في حالة عامة، لا يتطلب أن يكون بالضبط قائمة StuffBase، لذلك فمن أكثر مرونة.

هل يمكن أن تغلب على ذلك عن طريق نسخ أول قائمة من Stuff1 إلى قائمة StuffBase، لجعلها متوافقة مع الإصدار غير عام. ولكن بعد ذلك نفترض كان لديك طريقة:

List<T> TransformList<T>(List<T> input) where T : StuffBase
{
    List<T> output = new List<T>();

    foreach (T item in input)
    {
        // examine item and decide whether to discard it,
        // make new items, whatever
    }

    return output;
}

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

في المثال الذي قدمته ليس هناك فرق ولكن في محاولة على ما يلي:

List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);

والمكالمة الأولى يعمل بشكل جيد ولكن لا ثانية واحدة لا ترجمة بسبب التغاير عام

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