لماذا من المستحيل تجاوز خاصية getter فقط وإضافة أداة ضبط؟[مغلق]

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

سؤال

لماذا لا يُسمح برمز C# التالي:

public abstract class BaseClass
{
    public abstract int Bar { get;}
}

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get { return 0; }
        set {}
    }
}

CS0546 "ConcreteClass.Bar.set":لا يمكن التجاوز لأن 'BaseClass.Bar' لا يحتوي على أداة وصول يمكن تجاوزها

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

المحلول

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

أنا مع مايكروسوفت في هذا الشأن.
لنفترض أنني مبرمج جديد طُلب منه البرمجة وفقًا لاشتقاق Baseclass.أكتب شيئًا يفترض أنه لا يمكن الكتابة إلى Bar (نظرًا لأن Baseclass ينص صراحةً على أنه خاصية الحصول عليها فقط).الآن مع اشتقاقك، قد ينكسر الكود الخاص بي.على سبيل المثال

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

نظرًا لأنه لا يمكن تعيين Bar وفقًا لواجهة BaseClass، يفترض BarProvider أن التخزين المؤقت هو أمر آمن - حيث لا يمكن تعديل Bar.ولكن إذا كان set ممكنًا في الاشتقاق، فمن الممكن أن تقدم هذه الفئة قيمًا قديمة إذا قام شخص ما بتعديل خاصية Bar الخاصة بالكائن _source خارجيًا.النقطة هي "كن منفتحًا، وتجنب القيام بأشياء مخادعة ومفاجأة الناس'

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

نصائح أخرى

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

public override int MyProperty { get { ... } set { ... } }

هو واضح تماما أن كلا من get و ال set يتم التجاوزات.لا يوجد set في الفئة الأساسية، لذلك يشكو المترجم.مثلما لا يمكنك تجاوز طريقة غير محددة في الفئة الأساسية، لا يمكنك تجاوز أداة الضبط أيضًا.

قد تقول أن المترجم يجب أن يخمن نيتك ويطبق التجاوز فقط على الطريقة التي يمكن تجاوزها (أي.getter في هذه الحالة)، ولكن هذا يتعارض مع أحد مبادئ تصميم C# - حيث يجب على المترجم ألا يخمن نواياك، لأنه قد يخمن خطأً دون علمك.

أعتقد أن بناء الجملة التالي قد يكون جيدًا، ولكن كما يقول إريك ليبرت، فإن تنفيذ حتى ميزة بسيطة مثل هذه لا يزال يتطلب قدرًا كبيرًا من الجهد...

public int MyProperty
{
    override get { ... }
    set { ... }
}

أو، بالنسبة للخصائص التي يتم تنفيذها تلقائيًا،

public int MyProperty { override get; set; }

لقد عثرت على نفس المشكلة اليوم وأعتقد أن لدي سبب وجيه جدًا لرغبتي في ذلك.

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

وفيما يلي مثال مبسط لما احتاجه اليوم.انتهى بي الأمر إلى إضافة أداة ضبط في واجهتي فقط للتغلب على هذا الأمر.السبب وراء إضافة أداة الضبط وعدم إضافة طريقة SetProp، على سبيل المثال، هو أن أحد التطبيقات المعينة للواجهة يستخدم DataContract/DataMember لتسلسل Prop، والذي كان من الممكن أن يصبح معقدًا بلا داع إذا اضطررت إلى إضافة خاصية أخرى لهذا الغرض فقط من التسلسل.

interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

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

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

انه ممكن

ليرة تركية ؛ د- يمكنك تجاوز طريقة الحصول فقط باستخدام أداة ضبط إذا كنت تريد ذلك.انها في الأساس مجرد:

  1. إنشاء new الممتلكات التي لديها كل من أ get و أ set باستخدام نفس الاسم.

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

وهذا يتيح لنا تجاوز الخصائص مع get/set حتى لو كانوا يفتقرون إلى واحد في تعريفهم الأساسي.

كمكافأة، يمكنك أيضًا تغيير نوع الإرجاع إذا كنت تريد ذلك.

  • إذا كان التعريف الأساسي get-فقط، فيمكنك استخدام نوع إرجاع أكثر اشتقاقًا.

  • إذا كان التعريف الأساسي set-فقط، إذن يمكنك استخدام نوع إرجاع أقل اشتقاقًا.

  • إذا كان التعريف الأساسي بالفعل get/set, ، ثم:

    • يمكنك استخدام نوع إرجاع أكثر اشتقاقًا لو لقد فعلتها set-فقط؛

    • يمكنك استخدام نوع إرجاع أقل اشتقاقًا لو لقد فعلتها get-فقط.

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

الموقف:موجودة مسبقا get- الملكية فقط

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

public abstract class A                     // Pre-existing class; can't modify
{
    public abstract int X { get; }          // You want a setter, but can't add it.
}
public class B : A                          // Pre-existing class; can't modify
{
    public override int X { get { return 0; } }
}

مشكلة:لا أستطيع override ال get-فقط مع get/set

اتريد override مع get/set الملكية، ولكن لن يتم تجميعها.

public class C : B
{
    private int _x;
    public override int X
    {
        get { return _x; }
        set { _x = value; }   //  Won't compile
    }
}

حل:استخدم abstract طبقة المتوسطة

بينما لا تستطيع مباشرة override مع get/set الممتلكات، أنت يستطيع:

  1. إنشاء new get/set الممتلكات التي تحمل نفس الاسم.

  2. override القديم get طريقة مع وصول إلى الجديد get طريقة لضمان الاتساق.

لذلك، أولا تكتب abstract طبقة المتوسطة:

public abstract class C : B
{
    //  Seal off the old getter.  From now on, its only job
    //  is to alias the new getter in the base classes.
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}

ثم تكتب الفصل الذي لم يتم تجميعه مسبقًا.سيتم تجميعها هذه المرة لأنك لست كذلك في الواقع override'في get-ملكية فقط؛بدلاً من ذلك، تقوم باستبداله باستخدام new الكلمة الرئيسية.

public class D : C
{
    private int _x;
    public new virtual int X { get { return this._x; } set { this._x = value; } }

    //  Ensure base classes (A,B,C) use the new get method.
    protected sealed override int XGetter { get { return this.X; } }
}

نتيجة:كل شيء يعمل!

ومن الواضح أن هذا يعمل على النحو المنشود D.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

test.X = 7;
Print(test.X);      // Prints "7", as intended.

كل شيء لا يزال يعمل كما هو مقصود عند المشاهدة D كواحدة من فئاتها الأساسية، على سبيل المثال. A أو B.لكن السبب وراء نجاحه قد يكون أقل وضوحًا.

var test = new D() as B;
//test.X = 7;       // This won't compile, because test looks like a B,
                    // and B still doesn't provide a visible setter.

ومع ذلك، فإن تعريف الفئة الأساسية لـ get لا يزال يتم تجاوزه في النهاية من خلال تعريف الفئة المشتقة لـ get, ، لذا فهو لا يزال متسقًا تمامًا.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

var baseTest = test as A;
Print(test.X);      // Prints "7", as intended.

مناقشة

هذه الطريقة تسمح لك بإضافة set طرق ل get- خصائص فقط.يمكنك أيضًا استخدامه للقيام بأشياء مثل:

  1. تغيير أي خاصية إلى أ get-فقط، set- فقط، أو get-و-set الملكية، بغض النظر عما كانت عليه في فئة أساسية.

  2. تغيير نوع الإرجاع للطريقة في الفئات المشتقة.

تتمثل العيوب الرئيسية في أن هناك المزيد من البرمجة التي يجب القيام بها والمزيد abstract class في شجرة الميراث.قد يكون هذا مزعجًا بعض الشيء بالنسبة للمنشئين الذين يأخذون المعلمات لأنه يجب نسخها/لصقها في الطبقة المتوسطة.

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

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

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

على أية حال، لقد استخدمت إحدى الاستراتيجيات التالية للتغلب على هذا القيد:كلاهما أقل من الكمال، لكنهما سيسمحان لك بتجاوز هذا النقص اللغوي:

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }

ربما يمكنك التغلب على المشكلة عن طريق إنشاء خاصية جديدة:

public new int Bar 
{            
    get { return 0; }
    set {}        
}

int IBase.Bar { 
  get { return Bar; }
}

أستطيع أن أفهم كل نقاطك، ولكن على نحو فعال، تصبح الخصائص التلقائية لـ C# 3.0 عديمة الفائدة في هذه الحالة.

لا يمكنك فعل أي شيء كهذا:

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get;
        private set;
    }
}

يجب ألا تقيد IMO وC# مثل هذه السيناريوهات.وتقع على عاتق المطور مسؤولية استخدامه وفقًا لذلك.

تكمن المشكلة في أنه لأي سبب كان قررت Microsoft أنه يجب أن يكون هناك ثلاثة أنواع مختلفة من الخصائص:للقراءة فقط، والكتابة فقط، والقراءة والكتابة، وقد يوجد واحد منها فقط بتوقيع معين في سياق معين؛لا يجوز تجاوز الخصائص إلا من خلال الخصائص المعلنة بشكل مماثل.للقيام بما تريد، سيكون من الضروري إنشاء خاصيتين بنفس الاسم والتوقيع - أحدهما للقراءة فقط، والآخر للقراءة والكتابة.

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

فيما يلي حل بديل لتحقيق ذلك باستخدام الانعكاس:

var UpdatedGiftItem = // object value to update;

foreach (var proInfo in UpdatedGiftItem.GetType().GetProperties())
{
    var updatedValue = proInfo.GetValue(UpdatedGiftItem, null);
    var targetpropInfo = this.GiftItem.GetType().GetProperty(proInfo.Name);
    targetpropInfo.SetValue(this.GiftItem, updatedValue,null);
}

بهذه الطريقة يمكننا تعيين قيمة الكائن على خاصية للقراءة فقط.قد لا تعمل في جميع السيناريوهات بالرغم من ذلك!

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


إذا كان السابق (تجاوز خاصية مجردة)

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

public int GetBar(){}

ولكن إذا لم يكن لديك أي سيطرة على ذلك، وكانت وظيفة الفئة الأساسية تقرأ من الملكية العامة الخاصة بها (غريب)، فما عليك سوى القيام بذلك:

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    private int _bar;
    public override int Bar
    {
        get { return _bar; }
    }
    public void SetBar(int value)
    {
        _bar = value;
    }
}

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

public abstract class BaseClass {
    protected int _bar;
    public int Bar { get { return _bar; } }
    protected void DoBaseStuff()
    {
        SetBar();
        //Do something with _bar;
    }
    protected abstract void SetBar();
}

public class ConcreteClass : BaseClass {
    protected override void SetBar() { _bar = 5; }
}

إذا كان الأخير (تجاوز خاصية الحصول على الفصل فقط)

كل خاصية غير مجردة لها أداة ضبط.وإلا فهو عديم الفائدة ولا يجب أن تهتم باستخدامه.ليس من الضروري أن تسمح لك Microsoft بفعل ما تريد.السبب هو:الواضع موجود بشكل أو بآخر، ويمكنك تحقيق ما تريد فيريي بسهولة.

الفئة الأساسية، أو أي فئة يمكنك من خلالها قراءة خاصية ما {get;}, ، لديه بعض نوع من الواضع المكشوف لتلك الخاصية.ستبدو البيانات الوصفية كما يلي:

public abstract class BaseClass
{
    public int Bar { get; }
}

لكن التنفيذ سيكون له طرفان من نطاق التعقيد:

الأقل تعقيدًا:

public abstract class BaseClass
{
    private int _bar;
    public int Bar { 
        get{
            return _bar;
        }}
    public void SetBar(int value) { _bar = value; }
}

الاكثر تعقيدا:

public abstract class BaseClass
{
    private int _foo;
    private int _baz;
    private int _wtf;
    private int _kthx;
    private int _lawl;

    public int Bar
    {
        get { return _foo * _baz + _kthx; }
    }
    public bool TryDoSomethingBaz(MyEnum whatever, int input)
    {
        switch (whatever)
        {
            case MyEnum.lol:
                _baz = _lawl + input;
                return true;
            case MyEnum.wtf:
                _baz = _wtf * input;
                break;
        }
        return false;
    }
    public void TryBlowThingsUp(DateTime when)
    {
        //Some Crazy Madeup Code
        _kthx = DaysSinceEaster(when);
    }
    public int DaysSinceEaster(DateTime when)
    {
        return 2; //<-- calculations
    }
}
public enum MyEnum
{
    lol,
    wtf,
}

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

في كلا الأخيرين والسابقين (خاتمة)

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

حل لمجموعة فرعية صغيرة فقط من حالات الاستخدام، ولكن مع ذلك:في الإصدار C# 6.0، تتم إضافة أداة الضبط "للقراءة فقط" تلقائيًا لخصائص getter فقط التي تم تجاوزها.

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    public override int Bar { get; }

    public ConcreteClass(int bar)
    {
        Bar = bar;
    }
}

لأنه على مستوى IL، تُترجم خاصية القراءة/الكتابة إلى طريقتين (getter وsetter).

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

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

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

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

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

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

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

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

وهذا يدعم أيضًا الملاحظة الصحيحة:"اجعل من الصعب قدر الإمكان ظهور سوء الفهم" بقلم جيشو.

هذا ليس مستحيلا.عليك ببساطة استخدام الكلمة الرئيسية "الجديدة" في الممتلكات الخاصة بك.على سبيل المثال،

namespace {
    public class Base {
        private int _baseProperty = 0;

        public virtual int BaseProperty {
            get {
                return _baseProperty;
            }
        }

    }

    public class Test : Base {
        private int _testBaseProperty = 5;

        public new int BaseProperty {
            get {
                return _testBaseProperty;
            }
            set {
                _testBaseProperty = value;
            }
        }
    }
}

ويبدو أن هذا النهج يرضي طرفي هذه المناقشة.يؤدي استخدام "جديد" إلى كسر العقد بين تطبيق الفئة الأساسية وتنفيذ الفئة الفرعية.يعد ذلك ضروريًا عندما يكون للفئة عقود متعددة (إما عبر الواجهة أو الفئة الأساسية).

أتمنى أن يساعدك هذا

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