C #: การเพิ่มกิจกรรมที่สืบทอด


144

ฉันมีคลาสฐานที่ประกอบด้วยเหตุการณ์ต่อไปนี้:

public event EventHandler Loading;
public event EventHandler Finished;

ในคลาสที่สืบทอดมาจากคลาสพื้นฐานนี้ฉันพยายามเพิ่มกิจกรรม:

this.Loading(this, new EventHandler()); // All we care about is which object is loading.

ฉันได้รับข้อผิดพลาดต่อไปนี้:

เหตุการณ์ 'BaseClass.Loading' สามารถปรากฏที่ด้านซ้ายมือของ + = หรือ - = (BaseClass ')

ฉันสมมติว่าฉันไม่สามารถเข้าถึงกิจกรรมเหล่านี้เหมือนกับสมาชิกที่สืบทอดอื่น ๆ


คำถามนี้ได้รับการตอบแล้วโดยเฟรดเดอ Gheysels แนะนำให้ใช้แบบฝึกหัดเดียวกันกับ Microsoft Docs: วิธีเพิ่มเหตุการณ์คลาสพื้นฐานในคลาสที่ได้รับ (คู่มือการเขียนโปรแกรม C #)เป็น "คู่มือการเขียนโปรแกรม C # อย่างเป็นทางการ"
Eliahu Aaron

คำตอบ:


160

สิ่งที่คุณต้องทำคือ:

ในคลาสพื้นฐานของคุณ (ที่คุณได้ประกาศเหตุการณ์) สร้างวิธีการป้องกันที่สามารถใช้ในการเพิ่มเหตุการณ์:

public class MyClass
{
   public event EventHandler Loading;
   public event EventHandler Finished;

   protected virtual void OnLoading(EventArgs e)
   {
       EventHandler handler = Loading;
       if( handler != null )
       {
           handler(this, e);
       }
   }

   protected virtual void OnFinished(EventArgs e)
   {
       EventHandler handler = Finished;
       if( handler != null )
       {
           handler(this, e);
       }
   }
}

(โปรดทราบว่าคุณควรเปลี่ยนวิธีการเหล่านั้นเพื่อตรวจสอบว่าคุณต้องเรียกใช้ตัวจัดการเหตุการณ์หรือไม่)

จากนั้นในคลาสที่สืบทอดจากคลาสพื้นฐานนี้คุณสามารถเรียกเมธอด OnFinished หรือ OnLoading เพื่อเพิ่มเหตุการณ์:

public AnotherClass : MyClass
{
    public void DoSomeStuff()
    {
        ...
        OnLoading(EventArgs.Empty);
        ...
        OnFinished(EventArgs.Empty);
    }
}

5
วิธีการเหล่านั้นควรได้รับการคุ้มครองเสมือนเว้นแต่จะมีเหตุผลบางอย่างที่ต้องทำ
Max Schmeling

6
ทำไมมันควรเป็นเสมือน? ฉันจะประกาศเสมือนถ้าผมต้องการผู้รับมรดกจะเปลี่ยนวิธีการจัดกิจกรรมที่ควรได้รับการเลี้ยงดู แต่ส่วนมากของเวลาที่ฉันเห็นเหตุผลที่จะทำเช่นนี้ไม่ ...
เฟรดเดอ Gheysels

4
แนวทางอย่างเป็นทางการ: msdn.microsoft.com/en-us/library/w369ty8x(VS.80).aspx
meandmycode

5
เกี่ยวกับการสร้างเมธอดเสมือนเพื่อให้ผู้สืบทอดสามารถแทนที่พฤติกรรมการเรียกใช้เหตุการณ์: มีกี่ครั้งที่คุณอยู่ในสถานการณ์ที่จำเป็น ถัดจากนั้น ในวิธีการ overriden คุณไม่สามารถเพิ่มเหตุการณ์ได้เนื่องจากคุณจะได้รับข้อผิดพลาดเดียวกับที่ TS กล่าวถึง
Frederik Gheysels

2
@ Verax ฉันทำตามเพราะเหตุผลคือเสียงขึ้นอยู่กับวิธีการใช้งานอีกครั้งและขยายได้ฉันคาดว่ารหัสจะเป็นฉันได้ให้แนวทางอย่างเป็นทางการเพื่อสำรองที่ .. ในขณะที่เขียนคุณยังใหม่มากกับ. NET Verax ดังนั้นจึงเป็นเรื่องน่างงที่คุณขุดสิ่งนี้ขึ้นมาเพื่อไม่เห็นด้วยกับประสบการณ์ 7 ปีของฉัน
meandmycode

123

คุณสามารถเข้าถึงเหตุการณ์ในคลาสที่ประกาศเนื่องจาก. NET สร้างตัวแปรอินสแตนซ์ส่วนตัวด้านหลังฉากที่เก็บผู้รับมอบสิทธิ์จริง ๆ ทำสิ่งนี้ ..

public event EventHandler MyPropertyChanged;

กำลังทำสิ่งนี้อยู่

private EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

และทำสิ่งนี้ ...

MyPropertyChanged(this, EventArgs.Empty);

จริง ๆ แล้วนี่ ...

myPropertyChangedDelegate(this, EventArgs.Empty);

ดังนั้นคุณสามารถ (เห็นได้ชัด) เท่านั้นเข้าถึงตัวแปรอินสแตนซ์ผู้แทนส่วนตัวจากภายในคลาสที่ประกาศ

การประชุมคือการให้สิ่งนี้ในชั้นเรียนที่ประกาศ ..

protected virtual void OnMyPropertyChanged(EventArgs e)
{
    EventHandler invoker = MyPropertyChanged;

    if(invoker != null) invoker(this, e);
}

จากนั้นคุณสามารถโทรOnMyPropertyChanged(EventArgs.Empty)จากที่ใดก็ได้ในคลาสนั้นหรือใต้ทายาทมรดกเพื่อเรียกใช้เหตุการณ์


ฉันมีปัญหาบางอย่างในการใช้งาน ... stackoverflow.com/q/10593632/328397
goodguys_activate

ฉันชอบคำตอบนี้เพราะอธิบายว่าทำไมคุณต้องใช้วิธีการแทนที่จะเป็นวิธีการ ทำได้ดีมาก
Cowboydan

8

ฉันสมมติว่าฉันไม่สามารถเข้าถึงกิจกรรมเหล่านี้เหมือนกับสมาชิกที่สืบทอดอื่น ๆ

แม่นยำ. เป็นเรื่องธรรมดาที่จะให้ฟังก์ชั่นที่ได้รับการปกป้องOnXyzหรือRaiseXyzสำหรับแต่ละเหตุการณ์ในคลาสฐานเพื่อให้สามารถยกระดับจากคลาสที่สืบทอดมาได้ ตัวอย่างเช่น:

public event EventHandler Loading;

protected virtual void OnLoading() {
    EventHandler handler = Loading;
    if (handler != null)
        handler(this, EventArgs.Empty);
}

เรียกว่าในระดับที่สืบทอด:

OnLoading();

0

คุณสามารถลองด้วยวิธีนี้ได้ผลกับฉัน:

public delegate void MyEventHaldler(object sender, EventArgs e);

public class B
{
    public virtual event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

public class D : B
{
    public override event MyEventHaldler MyEvent;
    protected override void OnChanged(EventArgs e)
    {
        if (MyEvent != null)
            MyEvent(this, e);
    }
}

2
โปรดทราบว่าบทความนี้เตือนไม่ให้ประกาศเหตุการณ์เสมือนและแทนที่พวกเขาเนื่องจากอ้างว่าคอมไพเลอร์ไม่จัดการเรื่องนี้อย่างถูกต้อง: msdn.microsoft.com/en-us/library/hy3sefw3.aspx
สาธารณะ

0

ที่จะไม่ฟื้นด้ายเก่า แต่ในกรณีที่มีใครกำลังดูสิ่งที่ฉันทำคือ

protected EventHandler myPropertyChangedDelegate;

public event EventHandler MyPropertyChanged
{
    add { myPropertyChangedDelegate += value; }
    remove { myPropertyChangedDelegate -= value; }
}

สิ่งนี้ช่วยให้คุณสามารถสืบทอดเหตุการณ์ในคลาสที่ได้รับมาเพื่อให้คุณสามารถเรียกใช้โดยไม่จำเป็นต้องตัดเมธอดขณะที่รักษา + = ไวยากรณ์ไว้ ฉันเดาว่าคุณยังสามารถทำได้ด้วยวิธีการห่อถ้าคุณทำ

public event EventHandler MyPropertyChanged
{
   add { AddDelegate(value); }
   remove { RemoveDelegate(value); }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.