มีการเพิ่มตัวจัดการเหตุการณ์แล้วหรือยัง?


183

มีวิธีบอกหรือไม่ว่ามีการเพิ่มตัวจัดการเหตุการณ์ไปยังวัตถุหรือไม่? ฉันจัดลำดับรายการวัตถุเข้า / ออกจากสถานะเซสชันเพื่อให้เราสามารถใช้สถานะเซสชันที่ใช้ SQL ได้ ... เมื่อวัตถุในรายการเปลี่ยนคุณสมบัติต้องมีการตั้งค่าสถานะซึ่งตัวจัดการเหตุการณ์ดูแลอย่างถูกต้องก่อน . อย่างไรก็ตามเมื่อวัตถุถูก deserialized มันไม่ได้รับตัวจัดการเหตุการณ์

ด้วยความรำคาญเล็กน้อยฉันเพิ่งเพิ่มตัวจัดการเหตุการณ์ในคุณสมบัติรับที่เข้าถึงวัตถุ มันได้รับการเรียกแล้วตอนนี้ซึ่งยอดเยี่ยมยกเว้นว่ามันจะถูกเรียกเช่น 5 ครั้งดังนั้นฉันคิดว่าตัวจัดการจะเพิ่มเข้ามาทุกครั้งที่เข้าถึงวัตถุ

มันปลอดภัยพอที่จะเพิกเฉย แต่ฉันต้องการทำให้มันสะอาดกว่าเดิมโดยการตรวจสอบเพื่อดูว่ามีการเพิ่มตัวจัดการแล้วหรือไม่ดังนั้นฉันจะทำเพียงครั้งเดียว

เป็นไปได้ไหม

แก้ไข: ฉันไม่จำเป็นต้องมีการควบคุมอย่างเต็มที่ว่าจะเพิ่มตัวจัดการเหตุการณ์ใดดังนั้นเพียงแค่ตรวจสอบว่า null ไม่ดีพอ


คำตอบ:


123

จากนอกคลาสที่นิยามตามที่ @Telos กล่าวถึงคุณสามารถใช้ EventHandler ทางด้านซ้ายมือของ a +=หรือ a -=เท่านั้น ดังนั้นถ้าคุณมีความสามารถในการปรับเปลี่ยนคลาสที่กำหนดคุณสามารถจัดเตรียมวิธีการตรวจสอบโดยการตรวจสอบว่าตัวจัดการเหตุการณ์null- ถ้าเป็นเช่นนั้นแล้วไม่มีการเพิ่มตัวจัดการเหตุการณ์ ถ้าไม่เช่นนั้นอาจจะและคุณก็สามารถห่วงผ่านค่าใน Delegate.GetInvocationList หากหนึ่งเท่ากับผู้รับมอบสิทธิ์ที่คุณต้องการเพิ่มเป็นตัวจัดการเหตุการณ์คุณก็รู้ว่ามันมี

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

และสิ่งนี้สามารถแก้ไขได้อย่างง่ายดายเพื่อให้เป็น "เพิ่มตัวจัดการหากไม่มี" หากคุณไม่สามารถเข้าถึงอวัยวะภายในของชั้นเรียนที่เปิดเผยกิจกรรมคุณอาจต้องสำรวจ-=และ+=ตามที่ @Lou Franco แนะนำ

อย่างไรก็ตามคุณควรตรวจสอบวิธีการว่าจ้างและการรื้อถอนวัตถุเหล่านี้ให้ดีขึ้นเพื่อดูว่าคุณไม่สามารถหาวิธีติดตามข้อมูลนี้ได้ด้วยตัวเองหรือไม่


7
สิ่งนี้ไม่ได้รวบรวม EventHandler สามารถอยู่ทางด้านซ้ายมือของ + = หรือ - =
CodeRedick

2
ลบการลงคะแนนเมื่อมีคำอธิบายเพิ่มเติม สถานะ SQL นั้นทำลายความคิดทั้งหมดที่นี่ ... :(
CodeRedick

1
ขอบคุณการค้นหาแบลร์และดังนั้นสิ่งที่ฉันกำลังมองหา (น่ารำคาญคุณไม่สามารถทำได้นอกห้องเรียน)
George Mauer

3
ปัญหาเกิดขึ้นส่วนใหญ่ในขณะที่เปรียบเทียบผู้รับมอบสิทธิ์เพื่อความเท่าเทียมกัน ดังนั้นให้ใช้Delegate.Equals(objA, objB)ถ้าคุณต้องการตรวจสอบตัวแทนเดิม if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName)มิฉะนั้นเปรียบเทียบคุณสมบัติเป็นรายบุคคลเช่น
Sanjay

2
รหัสนี้ไม่ทำงานใน WinForm มันเคร่งครัดสำหรับ ASP.NET หรือเปล่า?
jp2code

213

ฉันเพิ่งมาถึงสถานการณ์ที่คล้ายกันซึ่งฉันต้องลงทะเบียนตัวจัดการสำหรับเหตุการณ์เพียงครั้งเดียว ฉันพบว่าคุณสามารถยกเลิกการลงทะเบียนอย่างปลอดภัยก่อนจากนั้นลงทะเบียนอีกครั้งแม้ว่าตัวจัดการจะไม่ได้ลงทะเบียนเลย:

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

โปรดทราบว่าการทำเช่นนี้ทุกครั้งที่คุณลงทะเบียนตัวจัดการจะทำให้แน่ใจว่าตัวจัดการของคุณลงทะเบียนเพียงครั้งเดียว ฟังดูเหมือนเป็นการปฏิบัติที่ดีสำหรับฉัน :)


9
ดูเหมือนเสี่ยง หากเหตุการณ์ถูกไล่ออกหลังจากลบตัวจัดการและก่อนที่จะเพิ่มกลับมันจะพลาด
Jimmy

27
แน่ใจ คุณหมายความว่ามันไม่ปลอดภัย แต่นั่นอาจเป็นปัญหาเมื่อใช้หลายเธรดหรือสิ่งที่คล้ายซึ่งไม่ใช่เรื่องปกติ ในกรณีส่วนใหญ่สิ่งนี้ควรจะดีพอสำหรับความเรียบง่าย
alf

6
เรื่องนี้ทำให้ฉันรำคาญใจ เพียงเพราะคุณยังไม่ได้สร้างเธรดในรหัสของคุณอย่างชัดเจนนั่นไม่ได้หมายความว่าไม่มีหลายเธรดหรือว่าจะไม่ถูกเพิ่มในภายหลัง ทันทีที่คุณ (หรือคนอื่น ๆ ในทีมอาจมีอีกหลายเดือนต่อมา) เพิ่มเธรดผู้ปฏิบัติงานหรือตอบสนองต่อทั้ง UI และการเชื่อมต่อเครือข่ายสิ่งนี้จะเปิดประตูสู่เหตุการณ์ที่ลดลงอย่างต่อเนื่อง
Technophile

1
ฉันเชื่อว่าหน้าต่างเวลาระหว่างการลบและการเพิ่มอีกครั้งมีขนาดเล็กมากซึ่งไม่น่าจะมีเหตุการณ์ที่ไม่ได้เกิดขึ้นจริง แต่ก็ยังเป็นไปได้
Alisson

2
หากคุณกำลังใช้สิ่งนี้เช่นการอัปเดต UI ฯลฯ แล้วมันเป็นงานที่ไม่สำคัญความเสี่ยงก็โอเค ถ้านี่คือสำหรับการจัดการแพ็คเก็ตเครือข่าย ฯลฯ ฉันจะไม่ใช้มัน
ม้วน

18

หากนี่เป็นตัวจัดการเดียวคุณสามารถตรวจสอบเพื่อดูว่าเหตุการณ์นั้นเป็นโมฆะหรือไม่หากไม่ใช่ตัวจัดการนั้นจะถูกเพิ่มเข้าไป

ฉันคิดว่าคุณสามารถโทร - = ในเหตุการณ์ด้วยตัวจัดการของคุณแม้ว่าจะไม่ได้เพิ่ม (ถ้าไม่คุณสามารถจับได้) - เพื่อให้แน่ใจว่ามันไม่ได้อยู่ในนั้นก่อนเพิ่ม


3
ตรรกะนี้จะแตกทันทีที่มีการจัดการเหตุการณ์ที่อื่นเช่นกัน
bugged87

6

ตัวอย่างนี้แสดงวิธีใช้เมธอด GetInvocationList () เพื่อดึงผู้รับมอบสิทธิ์ไปยังตัวจัดการทั้งหมดที่ถูกเพิ่ม หากคุณกำลังมองหาว่ามีการเพิ่มตัวจัดการ (ฟังก์ชั่น) ที่เฉพาะเจาะจงจากนั้นคุณสามารถใช้อาร์เรย์

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

คุณสามารถตรวจสอบคุณสมบัติต่าง ๆ ในคุณสมบัติวิธีการของผู้รับมอบสิทธิ์เพื่อดูว่ามีการเพิ่มฟังก์ชันเฉพาะหรือไม่

หากคุณต้องการตรวจสอบว่ามีไฟล์แนบเพียงอันเดียวคุณสามารถทดสอบค่าว่าง


GetInvocationList () ไม่ได้เป็นสมาชิกของชั้นเรียนของฉัน ในความเป็นจริงฉันไม่สามารถหาวิธีการนี้ในวัตถุหรือตัวจัดการใด ๆ ที่ฉันสามารถเข้าถึง ...
CodeRedick

ฉันได้ลองแล้วเช่นกันเห็นได้ชัดว่าคุณสามารถเข้าถึงเหตุการณ์เช่นนั้นจากภายในห้องเรียน ฉันทำสิ่งนี้โดยทั่วไปและอย่างที่คนอื่น ๆ ได้กล่าวถึงตัวจัดการเหตุการณ์อาจจะหลงทางอยู่ดี ขอขอบคุณสำหรับการชี้แจง!
CodeRedick

4

ถ้าฉันเข้าใจปัญหาของคุณอย่างถูกต้องคุณอาจมีปัญหาที่ใหญ่กว่า คุณบอกว่าวัตถุอื่น ๆ อาจสมัครสมาชิกเหตุการณ์เหล่านี้ เมื่อวัตถุถูกทำให้เป็นอนุกรมและ deserialized วัตถุอื่น ๆ (วัตถุที่คุณไม่สามารถควบคุมได้) จะสูญเสียตัวจัดการเหตุการณ์

หากคุณไม่กังวลเกี่ยวกับสิ่งนั้นการอ้างอิงถึงตัวจัดการเหตุการณ์ของคุณควรดีพอ หากคุณกังวลเกี่ยวกับผลข้างเคียงของวัตถุอื่น ๆ ที่สูญเสียเครื่องมือจัดการเหตุการณ์คุณอาจต้องคิดกลยุทธ์การแคชใหม่


1
D'โอ้! ไม่เคยแม้แต่จะคิดว่า ... แม้ว่ามันควรจะชัดเจนเมื่อพิจารณาปัญหาเดิมของฉันคือผู้ดูแลของฉันหลงทาง
CodeRedick

2

ฉันเห็นด้วยกับคำตอบของ alf แต่การดัดแปลงเล็กน้อยเพื่อให้ใช้

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }

2

วิธีเดียวที่ได้ผลสำหรับฉันคือการสร้างตัวแปรบูลีนที่ฉันตั้งค่าเป็นจริงเมื่อฉันเพิ่มเหตุการณ์ ถ้าอย่างนั้นฉันจะถามว่า: ถ้าตัวแปรเป็นเท็จฉันจะเพิ่มเหตุการณ์

bool alreadyAdded = false;

ตัวแปรนี้สามารถเป็นโกลบอล

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}

0
EventHandler.GetInvocationList().Length > 0

2
สิ่งนี้จะไม่โยนเมื่อรายการ == เป็นโมฆะ?
Boris Callens

2
นอกคลาสที่เป็นเจ้าของตัวจัดการเหตุการณ์คุณสามารถใช้ - = และ + = เท่านั้น คุณไม่สามารถเข้าถึงกิจกรรม
tbergelt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.