委托与事件

委托与事件

一  委托

1-标准写法

定义一个委托:public delegate void EventHandler(object sender,EventArgs e);

 

2- 对委托的描述

1)首先委托是一个类(引用类型),因此在定义时最好将定义委托的位置与其他类平行

2)定义委托需要约定好返回值和参数;声明一个委托类型的变量,指向符合这个委托类型约束格式的函数(回调函数);对指向函数的格式(返回值和参数)进行了约束;

3)委托相当于一个函数容器,可以通过委托变量把需要的多个函数传出。


3-委托实现实例

namespace 委托
{
    //定义委托
    public delegate bool TestDelegate(string str);
    //委托调用类
    class Program
    {      
        static void Main(string[] args)
        {
            MyTest myTest = new MyTest();

            //****委托四种写法****
            //与回调函数进行绑定,绑定函数需要符合定义委托类型的格式
            //myTest.m_testDel = new TestDelegate(Say);
            myTest.m_testDel = Say;//上面方式的简写
            Func<string, bool> func = Say;  //使用泛型委托的写法Func<T>,bool为返回值类型
            Predicate<string> p = Say;//返回值bool且一个参数类型,符合Predicate<T>的格式
            
            bool isExcute = myTest.m_testDel("Tom");//委托执行回调函数

            if (isExcute)
            {
                //【多播委托】 委托容器内可以承载多个函数,但是第一次绑定需要实例化不能使用+=
                myTest.m_testDel -= Say;//注销函数
                myTest.m_testDel += Show;//绑定新函数
                myTest.m_testDel("OK");
            }

            //将委托封装在注册函数内
            Program program = new Program();
            program.OnExcute(Say, "Tom");
            //program.OnExcute(myTest.m_testDel,"Tom");方法注册到委托后,效果同上

            Console.ReadLine();
        }

        //所谓的注册函数就是一个普通函数,目的是为了封装委托
        private void OnExcute(TestDelegate testDel,string strText)
        {
            if (testDel != null)
            {
                testDel(strText);
            }
        }

        private static bool Say(string strName)
        {       
            Console.WriteLine(String.Format("Hi!  {0}", strName));
            return true;
        }

        private static bool Show(string strShow)
        {
            Console.WriteLine(String.Format("{0}  完成", strShow));
            return true;
        }
    }

    //委托封装类
    class MyTest
    {
        //委托类型成员变量
        public TestDelegate m_testDel;

        //内部封装,对委托进行保护
        public void WantSay(string name)
        {
            if (m_testDel != null)
            {
                m_testDel(name);
            }
        }
    }
}

4-泛型委托

描述:.Net已经定义了三种类型的泛型委托,分别是 Predicate、Action、Func。在使用linq的方法语法中,我们会经常遇到这些类型的参数。

优势:1)可以减少定义委托形式的重复操作,省去了定义委托类型的步骤。

         2)虽然delegate也可以写成泛型,例如public delegate void MyDelegate<T>(Targ),但是泛型委托的优势是传入参数  个数不固定可以灵活增减(参数个数范围0-16),参数的类型动态可变。

         3)有了泛型委托,我们就不用到处定义委托类型了,除非不满足需求,否则都应该优先使用内置的泛型委托。

三种类型:

1) Action无返回值,Action重载的参数个数有0-16个

Action<T> 例如: public delegate void Action<T>(Tobject)

2)Func有返回值。Func重载的参数个数有0-16个

Func<T> 例如:public delegate TResult Func<T, out TResult>(T arg);

注意:定义Func类型委托时,最后需要传入返回值的类型。例如:Func<int,int> action =PrintfMyAge; 返回值为int类型

3)Predicate

这是一种特殊的Func,因为只有一种形式且满足Func类型要求。

即public delegatebool Predicate<T>(T obj);有返回值且要求只能为bool类型,有参数且要求只能有一个泛型参数。

用处:用于封装定义了一组条件并确定指定对象是否符合这些条件的方法。

namespace 泛型委托
{
    class TestAction
    {
        //注释的这一行是Action的原型,这句可写可不写
        //注释的这一行是Action的原型,由此可看出Action无返回值
        public delegate void Action<T>(T arg);
               
        static void Main(string[] args)
        {
            Action<int> action = PrintfMyAge;
            action(18);

            Action<string> action2 = PrintfMyName;
            action2("Tom");
        }
        static void PrintfMyAge(int myAge)
        { Console.WriteLine("My Age is {0}", myAge); }
        static void PrintfMyName(string myName)
        { Console.WriteLine("My Name is {0}", myName); }
    }

    class TestFunc
    {
        //注释的这一行是Func的原型,这句可写可不写
        //注释的这一行是Func的原型,由此可看出Func有返回值
        public delegate TResult Func<T, out TResult>(T arg);
        static void Main(string[] args)
        {
            Func<int, int> action = PrintfMyAge;
            int n1 = action(18);
            Func<string, int> action2 = PrintfMyName;
            int n2 = action2("Tom");
            Console.ReadLine();
        }
        static int PrintfMyAge(int myAge)
        { Console.WriteLine("My Age is {0}", myAge); return 1; }
        static int PrintfMyName(string myName)
        { Console.WriteLine("My Name is {0}", myName); return 1; }
    }

    //自定义泛型委托
    public delegate void MyGenericDelegate<T>(T arg);
    class GenericDelegate
    {
        static void Main(string[] args)
        {
            MyGenericDelegate<string> strTarget = new MyGenericDelegate<string>(StringTarget);
            StringTarget("Some string data");

            MyGenericDelegate<int> intTarget = new MyGenericDelegate<int>(IntTarget);
            intTarget(9);

            Console.ReadLine();
        }

        static void StringTarget(string arg)
        {
            Console.WriteLine("arg in uppercase is : {0}", arg.ToUpper());
        }
        static void IntTarget(int arg)
        {
            Console.WriteLine("++arg is : {0}", ++arg);
        }
    }
}

二 事件

1-描述

事件是类型安全的委托,因此事件属于委托的子集,声明一个事件需要先定义一个委托在加上Event修饰符,也可以说它是一种具有事件性质的委托


2-事件的标准写法:

1)首先,声明一个具有事件性质的委托(可以省略,标准事件的委托类型为EventHandler该委托类型可以不用定义,.Net已经封装好可以直接使用)

事件执行所需要的参数:

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

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

sender数据源,表示触发事件的对象;//例如点击事件的对象是button;

e 是执行这个事件所需要的数据资源;//EventArgs是.Net定义好的事件资源类,可以继承。泛型EventHandler<TEventArgs>可设计自定义资源类。

 

2)然后,声明事件需要用event进行修饰

事件声明标准写法:事件名+EventHandler  定义一个事件习惯上以EventHandler结尾

声明一个事件 :public event EventHandler myEventHandler;

public event EventHandler<TEventArgs> myEventHandler;

 

 

3-委托与事件的区别及联系

委托与事件的区别:委托在任何地方都可以进行调用,事件则需要满足某些条件,且进行+=注册后才会触发,在外部事件接收类内注册,内部事件触发类内调用

委托与事件的联系:1)事件本质上在封装了多播委托(类似于属性封装字段),旨在保护委托,因为委托不安全在任何地方都可以调用,因此委托的本质是一个类型,事件的本质就是一个类型安全的委托。

                  2)属性是用Get_/Set_方法提供访问私有字段(非委托)的方法,事件就是用Add_/Remove_方法提供访问私有委托字段的方法。

                  3)委托的本质是引用类型(一个类),用于承载回调函数,委托用于实现回调机制;事件的本质是委托,事件是回调机制的一种应用。

委托与事件总结:

委托的作用:实现占位,在不知道将来要执行的方法的具体代码时,可以先用一个委托变量来代替方法调用(委托的返回值,参数列表要明确)。在委托调用之前,需要先给委托赋值,否则为null();

事件的作用:事件的作用与委托变量一样,只是在功能上比委托变量有更多的限制。(比如1.只能通过+=或-=来绑定方法(事件处理程序)2.只能在事件发送类的内部调用(触发)事件。)

 

4-事件实现步骤

要创建一个事件驱动的程序需要下面的步骤:

1)声明关于事件的委托;(使用.Net封装的委托类可以省略此步骤)

2)定义事件发送时所需要的附加信息类;(按照约定,所有的事件的附加信息都应该从EventArgs派生,但泛型EventHandler<TEventArgs>可以设计自定义类。)

3)声明事件;

4)编写触发事件的函数;(引发事件的代码常常被编写成一个函数,.NET约定这种函数的名称为“OnEventName”,比如OnAlarm()的函数。在函数内需判断事件变量是否为空)

5)创建事件处理程序;(在外部事件接收类内创建,返回值与入参要与事件委托格式一致)

6)注册事件处理程序;(与相关事件变量进行注册+=或注销-=)

7)在适当的条件下触发事件。


5-事件实现实例

namespace 事件
{
    //事件接收类
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new 事件.Person();
            person.age = "18";
            person.name = "Tom";
            person.tag = "1-1";
            MailMgr mail = new MailMgr(person);
            //委托注册
            mail.m_personDel = (sender, p) =>//Lamda创建符合委托类型格式的函数;定义的参数在执行委托时传入
                {
                    Console.WriteLine(string.Format("fax receive,from:{0} to:{1} content is:{2}", p.name, p.age, p.tag));
                };
            
            //委托可以在任何地方调用(与事件的区别)
            mail.m_personDel(null, person);
            //事件只能在定义事件的事件发送类内进行调用 6)注册事件处理程序;
            mail.personEventHandler += (sender, p) =>//5)创建事件处理程序;定义的参数在执行事件时传入
            {
                Console.WriteLine(string.Format("fax receive,from:{0} to:{1} content is:{2}", p.name, p.age, p.tag));
            };
            //7)在适当的条件下触发事件。调事件发送类内部的事件触发函数
            mail.OnMail();
           
            Console.ReadLine();
        }

    }
    //声明一个委托,与类的位置平行
    public delegate void PersonDel(object sender, Person p);
    //事件发送类
    class MailMgr
    {
        //3)声明事件;
        public event EventHandler<Person> personEventHandler;

        public PersonDel m_personDel;

        Person m_p = new Person();
        public MailMgr(Person p)
        {
            m_p = p;

        }
        //4)编写触发事件的函数;
        public void OnMail()
        {
            //不注册事件,无法回调函数
            if (personEventHandler != null)
            {
                //仅在事件发送类内部调用
                personEventHandler(this, m_p);
            }
        }


    }
    //2)定义事件发送时所需要的附加信息类;
    public class Person
    {
        public string name { set; get; }
        public string age { set; get; }
        public string tag { set; get; }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值