c# - تجاوز الحقول أو الخصائص في الفئات الفرعية




properties field (6)

لدي فئة أساسية مجردة وأريد أن أعلن عن حقل أو خاصية سيكون لها قيمة مختلفة في كل فئة ترث من هذا التصنيف الرئيسي.

أريد تعريفه في baseclass حتى أتمكن من الرجوع إليه في طريقة الفئة الأساسية - على سبيل المثال تجاوز ToString ليقول "هذا الكائن من نوع الخاصية / الحقل ". لدي ثلاث طرق أستطيع أن أراها للقيام بذلك ، لكنني كنت أتساءل - ما هي أفضل طريقة أو مقبولة للقيام بذلك؟ مبتدئ السؤال ، آسف.

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

abstract class Father
{
    abstract public int MyInt { get; set;}
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
        set { }
    }
}

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

abstract class Mother
{
    public int MyInt = 0;
}

class Daughter : Mother
{
    public int MyInt = 1;
}

الخيار 3
يمكنني استخدام حقل محمي وتعيين القيمة في المُنشئ. يبدو ذلك مرتبًا ولكن يعتمد على التأكد من أن المُنشئ يضبط هذا دائمًا ومع العديد من المنشئات المحملة بشكل زائد ، فهناك دائمًا احتمال ألا يحدد مسار الشفرة القيمة.

abstract class Aunt
{
    protected int MyInt;
}

class Niece : Aunt
{
    public Niece()
    {
        MyInt = 1;
    }
}

إنه سؤال نظري نوعًا ما وأعتقد أن الإجابة يجب أن تكون الخيار 1 حيث أنه الخيار الآمن الوحيد ولكني أتعامل مع C # وأردت أن أطلب من هؤلاء الأشخاص المزيد من الخبرة.


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

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

abstract class Mother
{
    private readonly myInt;
    public int MyInt { get { return myInt; } }

    protected Mother(int myInt)
    {
        this.myInt = myInt;
    }
}

class Daughter : Mother
{
    public Daughter() : base(1)
    {
    }
}

من المحتمل أن يكون هذا هو الأسلوب الأنسب إذا لم تتغير القيمة على مدار فترة الاستخدام.


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

أنا شخصياً أعتقد أن الخيار الأفضل هو 3 ؛ لأنه يوفر قيمة مركزية واضحة ، ويمكن أن يشار إليه داخليا من قبل الأطفال دون مشاحنات لتحديد حقولهم الخاصة - وهي مشكلة في الخيار 1.


سأذهب مع الخيار 3 ، ولكن لديك طريقة مجردة setMyInt التي تفرض على الفئات الفرعية للتنفيذ. بهذه الطريقة لن تواجه مشكلة طبقة مشتقة نسيان تعيينها في المُنشئ.

abstract class Base 
{
 protected int myInt;
 protected abstract void setMyInt();
}

class Derived : Base 
{
 override protected void setMyInt()
 {
   myInt = 3;
 }
}

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

abstract class Father
{
    abstract public int MyInt { get; }
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
    }
}

من بين الحلول الثلاثة ، الخيار الأول هو متعدد الأشكال .

الحقول من تلقاء نفسها لا يمكن تجاوزها. وهو بالضبط سبب إرجاع الخيار 2 لتحذير الكلمات الرئيسية الجديدة .

لا يعد حل التحذير إلحاق الكلمة الرئيسية "الجديدة" ، بل بتنفيذ الخيار 1.

إذا كنت بحاجة إلى أن يكون مجالك متعدد الأشكال فأنت بحاجة إلى لفه في خاصية.

الخيار 3 هو موافق إذا كنت لا تحتاج إلى سلوك متعدد الأشكال. على الرغم من ذلك ، يجب تذكر أنه عند الوصول إلى خاصية MyInt في وقت التشغيل ، فإن الفئة المشتقة لا تتحكم في القيمة التي يتم إرجاعها. الفئة الأساسية بحد ذاتها قادرة على إرجاع هذه القيمة.

هذه هي الطريقة التي قد يبدو بها تنفيذ متعدد الأشكال حقا لممتلكاتك ، مما يسمح للطبقات المشتقة بالتحكم .

abstract class Parent
{
    abstract public int MyInt { get; }
}

class Father : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "X" and return a value */ }
    }
}

class Mother : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "Y" and return a value */ }
    }
}

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

abstract class Aunt
{
    protected int MyInt;
    protected Aunt(int myInt)
    {
        MyInt = myInt;
    }

}

وبالطبع ، لا يزال أمامك خيار جعل الحقل خاصًا ثم ، بناءً على الحاجة ، يعرض أحد راكبي العقارات المحمية أو العامة.


يمكنك تحديد شيء مثل هذا:

abstract class Father
{
    //Do you need it public?
    protected readonly int MyInt;
}

class Son : Father
{
    public Son()
    {
        MyInt = 1;
    }
}

من خلال تعيين القيمة على هيئة readonly ، فإنها تضمن بقاء قيمة هذه الفئة دون تغيير طوال مدة الكائن.

أفترض أن السؤال التالي هو: لماذا تحتاجه؟







field