لماذا لا يمكنني الوصول إلى الأعضاء المحمية C # باستثناء هذا مثل هذا؟

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

سؤال

هذا الرمز:

abstract class C
{
    protected abstract void F(D d);
}

class D : C
{
    protected override void F(D d) { }

    void G(C c)
    {
        c.F(this);
    }
}

يولد هذا الخطأ:

لا يمكن الوصول إلى العضو المحمي "CF (D)" عبر مؤهل من النوع "C"؛ يجب أن يكون التصفيات من النوع "D" (أو مشتقة منه)

ماذا كانوا في العالم يفكرون؟ (هل يؤدي تغيير هذه القاعدة إلى كسر شيء ما؟) وهل هناك طريقة حولها جانبا من صنع F علني؟


تحرير: أنا الآن الحصول على سبب لماذا هذا (شكرا جريج) لكن ما زلت في حيرة بعض الشيء حسب عقلانية؛ منح:

class E : C
{
    protected override void F(D d) { }
}  

لماذا لا ينبغي ذلك د أن تكون قادرة على أن تكون قادرة على الاتصال EF؟


يتم تحرير رسالة الخطأ لذلك قد أضع مطبعي هناك.

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

المحلول

تعني الكلمة الأساسية "المحمية" أن النوع والأنواع التي تستمد من هذا النوع يمكنها الوصول إلى العضو. لا يوجد لديه أي علاقة مع C وبالتالي لا يمكن الوصول إلى العضو.

لديك خيارات بضع خيارات إذا كنت تريد أن تكون قادرا على الوصول إلى هذا العضو

  • اجعلها عامة
  • اجعلها الداخلية. سيسمح ذلك بأي نوع للوصول إلى العضو داخل نفس التجميع (أو التجميعات الأخرى يجب أن تضيف صديقا)
  • مشتق D من ج

تعديل

يطلق على هذا السيناريو في القسم 3.5.3 من المواصفات C #.

السبب غير مسموح به هو لأنه سيسمح بمكالمات التسلسل الهرمي عبر. تخيل أنه بالإضافة إلى D، كان هناك فئة قاعدة أخرى من C يسمى E. إذا كان يمكن أن يتيح الكود الخاص بك أنه من شأنه أن يسمح D للوصول إلى العضو EF هذا النوع من السيناريو غير مسموح به في C # (وأنا يصدق CLR لكنني لا أعرف 100٪).

Edit2. لماذا هذا سيء

تحذير، هذا هو رأيي

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

sealed class MyClass : C {
  override F(D d) { ... } 
}

فكر في ما يحدث إذا كانت F وظيفة حاسمة إلى حد ما. مع السلوك الحالي، يمكنني السبب في صحة صفي. بعد كل شيء، هناك حالتان فقط حيث سيتم استدعاء myclass.f.

  1. حيث تم استدعاء في ج
  2. حيث استدعاء ذلك صراحة في myclass

يمكنني فحص هذه المكالمات وتأتي إلى استنتاج معقول حول كيفية وظائف MyClass.

الآن، إذا كان C # يسمح الوصول عبر التسلسل الهرمي المحمي، فلا يمكنني تقديم أي ضمان. يمكن لأي شخص في مجموعة مختلفة تماما أن يأتي واكتشف من C. ثم يمكنهم الاتصال myclass.f في الإرادة. هذا يجعل من المستحيل تماما عن صحة صفي.

نصائح أخرى

السبب في أن هذا لا يعمل هو أن C # لا يسمح تسلسل التسلسل الهرمي عبر الأساليب المحمية. قل كان هناك فئة E التي المستمدة أيضا من C:

  C
 / \
D   E

ثم المرجع الذي تحاول استدعاء الطريقة قد يكون في الواقع مثيل للنوع E وبالتالي فإن الطريقة يمكن أن تحل في وقت التشغيل ل E.F. وبعد هذا غير مسموح به في C # كما D لا يمكن الاتصال Eالأساليب المحمية، ل E في فرع آخر من التسلسل الهرمي، أي

var d = new D();
var e = new E();
d.G(e); // oops, now this will call E.F which isn't allowed from D

هذا منطقي أن الكلمة الأساسية protected يعني العضو "يمكن الوصول إليها في فئتها والحالات الفئة المشتقة"و EF ليس عضوا في D.

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

سلسلة من المشاركات التي تشرح هذا من وجهة نظر C # المترجم:

يمكن تجاوز هذا القيد باستخدام طريقة محمية ثابتة:

abstract class C
{
    protected abstract void F (D d);

    // Allows calling F cross-hierarchy for any class derived from C
    protected static void F (C c, D d)
    {
        c.F(d);
    }
}

class D : C
{
    protected override void F (D d) { }

    void G (C c)
    {
        // c.F(this);
        F(c, this);
    }
}

هذا ليس مثاليا من وجهة نظر أمنية (يمكن لأي شخص أن يستمد من C)، ولكن إذا كان كل ما يهتم به هو طريقة الاختباء F من الواجهة العامة للفئة C, هذه الخدعة قد تكون مفيدة.

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

كي تختصر:

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

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

ملاحظة. يتم تنفيذ نفس السلوك في جافا.

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


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

مثال:

class Parent
{
    protected int foo = 0;
}

// Child extends from Parent
class Child : Parent
{
    public void SomeThing(Parent p)
    {
        // Here we're trying to access an instance's protected member.
        // So doing this...
        var foo = p.foo;
    }
}

// (this class has nothing to do with the previous ones)
class SomeoneElse
{
    public void SomeThing(Parent p)
    {
        // ...is the same as doing this (i.e. public access).
        var foo = p.foo++;
    }
}

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

يسمح لك بالوصول protected أعضاء من داخل الفصل، وليس من مثيل (نعم، نحن نعرف هذا):

class Child : Parent
{
    public void SomeThing()
    {
        // I'm allowed to modify parent's protected foo because I'm
        // doing so from within the class.
        foo++;
    }
}

أخيرا، من أجل الاكتمال، أنت قادر في الواقع الوصول إلى مثيل protected وحتى private الأعضاء فقط إذا كنت تفعل ذلك في نفس الفصل:

class Parent
{
    protected int foo = 0;

    private int bar = 0;

    public void SomeThing(Parent p)
    {
        // I'm allowed to access an instance's protected and private
        // members because I'm within Parent accessing a Parent instance
        var foo = p.foo;
        p.bar = 3;
    }
}

نعم هذا ممكن. سوف يكون لدينا على الأرجح مثل هذا المثال قريبا جدا.

للقيام بذلك يجب عليك القيام بما يلي:

  1. يرث النموذج الافتراضي (EditappointmentDialog) وقم بتخصيصك (يمكنك حتى استخدام مصمم WinForms لذلك).

الطبقة الجزئية العامة CustomappointedItItDialog: EditAppointmentDialog {private radcombobox cmbshowtimeas = null؛

    public CustomAppointmentEditDialog() 
    { 
        InitializeComponent(); 

        this.cmbShowTimeAs = this.Controls["cmbShowTimeAs"] as RadComboBox; 
    } 

    private void chkConfirmed_ToggleStateChanged(object sender, StateChangedEventArgs args) 
    { 
        this.cmbShowTimeAs.SelectedValue = (args.ToggleState == ToggleState.On) ? 
            (int)AppointmentStatus.Busy : (int)AppointmentStatus.Tentative; 
    } 
} 

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

  1. اشترك في تعيين الحدث RADSCHEDULER وبالتالي استبدال النموذج الافتراضي مع One Customized:

Private IeditappointmentDialog موعد الدينيتالوج = NULL؛

    protected override void OnLoad(EventArgs e) 
    { 
        base.OnLoad(e); 

        this.radScheduler1.AppointmentEditDialogShowing += new EventHandler<AppointmentEditDialogShowingEventArgs>(radScheduler1_AppointmentEditDialogShowing); 
    } 

    void radScheduler1_AppointmentEditDialogShowing(object sender, Telerik.WinControls.UI.AppointmentEditDialogShowingEventArgs e) 
    { 
        if (this.appointmentEditDialog == null) 
        { 
            this.appointmentEditDialog = new CustomAppointmentEditDialog(); 
        } 
        e.AppointmentEditDialog = this.appointmentEditDialog; 
    } 

آمل أن يساعد هذا. لا تتردد في كتابتي مرة أخرى إذا كانت لديك أسئلة أخرى.

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