基础_C# 事件

转载 2011年01月19日 17:02:00

C# 事件

event 事件
定义[修饰符] event void <委托类型> [接口类型.] <事件名称>;

event 关键字用于指定事件。类和结构使用事件将出现的可能影响对象状态的事件通知给对象。

向类中添加事件需要使用 event 关键字,并提供委托类型和事件名称。

事件使用委托来为触发时将调用的方法提供类型安全的封装。委托可以封装命名方法匿名方法

在下面的示例中,类 TestButton 包含事件 OnClick。派生自 TestButton 的类可以选择响应 OnClick 事件,并且定义了处理事件要调用的方法。可以以委托和匿名方法的形式指定多个处理程序。

// Declare the handler delegate for the event
public delegate void ButtonEventHandler();  

class TestButton
{
   
// OnClick is an event, implemented by a delegate ButtonEventHandler.
    public event ButtonEventHandler OnClick;

   
// A method that triggers the event:
    public void Click()
    {
        OnClick();
    }
}
// Create an instance of the TestButton class.
TestButton mb = new TestButton();

// Specify the method that will be triggered by the OnClick event.
mb.OnClick += new ButtonEventHandler(TestHandler);

// Specify an additional anonymous method.
mb.OnClick += delegate { System.Console.WriteLine("Hello, World!"); };

// Trigger the event
mb.Click();

事件概述

事件具有以下特点:

  • 事件是类用来通知对象需要执行某种操作的方式。

  • 尽管事件在其他时候(如信号状态更改)也很有用,事件通常还是用在图形用户界面中。

  • 事件通常使用委托事件处理程序进行声明。

  • 事件可以调用匿名方法来替代委托。有关更多信息,请参见匿名方法

 

引发事件

若要引发事件,类可以调用委托,并传递所有与事件有关的参数。然后,委托调用已添加到该事件的所有处理程序。如果该事件没有任何处理程序,则该事件为空。因此在引发事件之前,事件源应确保该事件不为空以避免 NullReferenceException。若要避免争用条件(最后一个处理程序会在空检查和事件调用之间被移除),在执行空检查和引发事件之前,事件源还应创建事件的一个副本。例如:

private void RaiseTestEvent()
{
    // Safely invoke an event:
    TestEventDelegate temp = TestEvent;

    if (temp != null)
    {
        temp(this, new System.EventArgs());
    }
}

订阅事件

要接收某个事件的类可以创建一个方法来接收该事件,然后向类事件自身添加该方法的一个委托。这个过程称为“订阅事件”。

首先,接收类必须具有与事件自身具有相同签名(如委托签名)的方法。然后,该方法(称为事件处理程序)可以采取适当的操作来响应该事件。例如

public class EventReceiver
{
    public void ReceiveTestEvent(object sender, System.EventArgs e)
    {
        System.Console.Write("Event received from ");
        System.Console.WriteLine(sender.ToString());
    }
}

每个事件可有多个处理程序。多个处理程序由源按顺序调用。如果一个处理程序引发异常,还未调用的处理程序则没有机会接收事件。由于这个原因,建议事件处理程序迅速处理事件并避免引发异常。

若要订阅事件,接收器必须创建一个与事件具有相同类型的委托,并使用事件处理程序作为委托目标。然后,接收器必须使用加法赋值运算符 (+=) 将该委托添加到源对象的事件中。例如:

public void Subscribe(EventSource source)
{
    TestEventDelegate temp = new TestEventDelegate(ReceiveTestEvent);
    source.TestEvent += temp;
}

若要取消订阅事件,接收器可以使用减法赋值运算符 (-=) 从源对象的事件中移除事件处理程序的委托。例如:

public void UnSubscribe(EventSource source)
{
    TestEventDelegate temp = new TestEventDelegate(ReceiveTestEvent);
    source.TestEvent -= temp;
}
声明事件访问器
使用 add 关键字和代码块添加事件的事件处理程序,使用 remove 关键字和代码块移除事件的事件处理程序。
public class EventSource2
{
    private TestEventDelegate TestEventHandlers;
    public event TestEventDelegate TestEvent
    {
        add
        {
            lock (TestEventHandlers)
            {
                TestEventHandlers += value;
            }
        }
        remove
        {
            lock (TestEventHandlers)
            {
                TestEventHandlers -= value;
            }
        }
    }
    private void RaiseTestEvent()
    {
        // Safely invoke an event.
        TestEventDelegate temp = TestEventHandlers;

        if (temp != null)
        {
            temp(this, new System.EventArgs());
        }
    }
}

前面示例中的 lock 语句用于防止多个线程同时操作事件列表。请参见 lock 语句和线程处理。

创建响应事件的控件

案例:某字处理器可能包含打开的文档的列表。每当该列表更改时,可能需要通知字处理器中的许多不同对象,以便能够更新用户界面。使用事件,维护文档列表的代码不需要知道需要通知谁,一旦文档列表发生了更改,将自动调用该事件,正确通知每个需要通知的对象。使用事件提高了程序的模块化程度。

ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_csref/html/2d20556a-0aad-46fc-845e-f85d86ea617a.htm

示例:

namespace TestCollections
{
    // A delegate type for hooking up change notifications.
    public delegate void ChangedEventHandler(object sender, System.EventArgs e);

    // A class that works just like ArrayList, but sends event
    // notifications whenever the list changes.
    public class ListWithChangedEvent : System.Collections.ArrayList
    {
        // An event that clients can use to be notified whenever the
        // elements of the list change.
        public event ChangedEventHandler Changed;

        // Invoke the Changed event; called whenever list changes
        protected virtual void OnChanged(System.EventArgs e)
        {
            if (Changed != null)
            {
                Changed(this, e);
            }
        }

        // Override some of the methods that can change the list;
        // invoke event after each
        public override int Add(object value)
        {
            int i = base.Add(value);
            OnChanged(System.EventArgs.Empty);
            return i;
        }

        public override void Clear()
        {
            base.Clear();
            OnChanged(System.EventArgs.Empty);
        }

        public override object this[int index]
        {
            set
            {
                base[index] = value;
                OnChanged(System.EventArgs.Empty);
            }
        }
    }
}

namespace TestEvents
{
    using TestCollections;

    class EventListener
    {
        private ListWithChangedEvent m_list;

        public EventListener(ListWithChangedEvent list)
        {
            m_list = list;

            // Add "ListChanged" to the Changed event on m_list:
            m_list.Changed += new ChangedEventHandler(ListChanged);
        }

        // This will be called whenever the list changes.
        private void ListChanged(object sender, System.EventArgs e)
        {
            System.Console.WriteLine("This is called when the event fires.");
        }

        public void Detach()
        {
            // Detach the event and delete the list
            m_list.Changed -= new ChangedEventHandler(ListChanged);
            m_list = null;
        }
    }

    class Test
    {
        // Test the ListWithChangedEvent class.
        static void Main()
        {
            // Create a new list.
            ListWithChangedEvent list = new ListWithChangedEvent();

            // Create a class that listens to the list's change event.
            EventListener listener = new EventListener(list);

            // Add and remove items from the list.
            list.Add("item 1");
            list.Clear();
            listener.Detach();
        }
    }
}
.NET Framework 指南指示用于事件的委托类型应采用两个参数:“对象源”参数(用于指示事件源)和特定于事件的参数(它封装有关事件的其他任何信息)。特定于事件的参数应从 EventArgs 类派生。对于不使用任何附加信息的事件,.NET Framework 提供了 EventHandler 类
如上面例子中修改成 .Net Framework 规则
namespace TestCollections
{
    // A class that works just like ArrayList, but sends event
    // notifications whenever the list changes:
    public class ListWithChangedEvent : System.Collections.ArrayList
    {
        // An event that clients can use to be notified whenever the
        // elements of the list change:
        public event System.EventHandler Changed;

        // Invoke the Changed event; called whenever list changes:
        protected virtual void OnChanged(System.EventArgs e)
        {
            if (Changed != null)
            {
                Changed(this, e);
            }
        }

        // Override some of the methods that can change the list;
        // invoke event after each:
        public override int Add(object value)
        {
            int i = base.Add(value);
            OnChanged(System.EventArgs.Empty);
            return i;
        }

        public override void Clear()
        {
            base.Clear();
            OnChanged(System.EventArgs.Empty);
        }

        public override object this[int index]
        {
            set
            {
                base[index] = value;
                OnChanged(System.EventArgs.Empty);
            }
        }
    }
}
在接口中声明一个事件,然后在类中实现该事件
public delegate void TestDelegate();   // delegate declaration

public interface ITestInterface
{
    event TestDelegate TestEvent;
    void FireAway();
}

public class TestClass : ITestInterface
{
    public event TestDelegate TestEvent;

    public void FireAway()
    {
        if (TestEvent != null)
        {
            TestEvent();
        }
    }
}

public class MainClass
{
    static private void F()
    {
        System.Console.WriteLine("This is called when the event fires.");
    }

    static void Main()
    {
        ITestInterface i = new TestClass();

        i.TestEvent += new TestDelegate(F);
        i.FireAway();
    }
}
实现两个具有同名事件的接口

当要实现两个具有同名事件的接口时,也需要用到事件属性。在这种情况下,必须使用显式实现事件属性

但是,在显式地实现接口中的事件时,您需要提供添加和移除方法。

jQuery基础——事件篇

jQuery基础——事件篇鼠标事件click与dbclick事件click方法监听用户单击操作,dbclick监听用户双击操作,这两个方法类似。 dbclick与click事件不同的是:click事...
  • winfredzen
  • winfredzen
  • 2016年05月12日 16:11
  • 6437

关于c#中的事件

c#事件学习
  • Joyhen
  • Joyhen
  • 2013年01月14日 11:32
  • 9185

[C#基础]委托与事件

原文链接:http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html .Net Framework中的委托与事件 尽管上面...
  • lyh916
  • lyh916
  • 2015年05月16日 23:09
  • 777

C#基础-委托与事件

委托(delegate)是一种数据结构,提供类似C++中函数指针的功能。不同的是,C++的函数指针只能够指向静态的方法,而委托除了可以指向静态的方法之外,还可以指向对象实例的方法。其实,最大的差别在于...
  • qinguang3
  • qinguang3
  • 2017年04月27日 19:34
  • 105

C#基础-委托与事件

委托与事件是一堆相互关联的概念,委托是一种引用类型,可用于封闭命名方法或你们方法;事件可以使类或对象想其他类或对象通知发生的相关事情。因为灵活的事件处理要求将事件的相应分派给相应的事件处理方法,而C#...
  • a1b2c300
  • a1b2c300
  • 2013年12月04日 21:26
  • 347

【基础】C# 中的委托和事件

引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去...
  • antchen88
  • antchen88
  • 2017年11月28日 14:18
  • 44

快速理解C#中的委托与事件

以下内容只是个人理解,仅供参考。 什么是委托? 先看最简单的委托例子: namespace DelegateTest { public delegate void MessageDele...
  • Wiiix
  • Wiiix
  • 2016年05月20日 20:02
  • 5310

Unity3D游戏开发中的C#事件

Unity3D是现在越来越流行的3D游戏引擎,它支持JavaScript,c#和Boo语言。其中C#有一个内置的事件机制,为了响应一个GameObject的事件分发,你通常要建立一个脚本继承MonoB...
  • book_longssl
  • book_longssl
  • 2015年01月31日 15:00
  • 1182

C# 自定义事件

C#的事件demo
  • xiaoguidangjiajia
  • xiaoguidangjiajia
  • 2016年01月05日 17:59
  • 553

C#中的委托和事件

C#中的委托和事件   前言   委托和事件在.NET框架中应用的很广泛,但是楼主因为知识尚浅,用到的不多,但是听前辈们说,这个委托和事件就像两座大山,只要翻过了这...
  • shanyongxu
  • shanyongxu
  • 2016年02月25日 17:09
  • 1102
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基础_C# 事件
举报原因:
原因补充:

(最多只允许输入30个字)