黑马程序员:C#基础篇(三)委托与事件

来源:互联网 发布:arm linux gcc 安装 编辑:程序博客网 时间:2024/05/01 07:44

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

委托与事件

1、委托

委托是一种引用方法的类型,委托类型派生自.NET Framework 中的Delegate类,它是密封的,不能从Delegate中派生委托类型,也不能从中派生自定义类型。与委托的签名(由返回类型和参数构成)匹配的任何方法都可以分配给委托,这样既可通过编程的方式来更改方法的调用,也可以实现在现有类中插入代码(既通过委托传递方法)。

delegate void EventHandler(object sender,EventArgs e)

委托具有以下特点:

(1)类型安全

(2)允许将方法当做参数传递

(3)可用于定义回调方法

(4)可以链接在一起(可以对一个事件调用多个方法)

(5)方法不需要与委托签名精准匹配

说到委托,不得不说匿名方法,委托提供运算符和方法来添加或者删除目标方法,可以广泛地应用于事件、回掉、异步调用、多线程等,然而有时候仅仅为了使用一个委托,不得不创建一个类或方法,这个时候就可以用到匿名方法。

    class SomeClass    {        delegate void SomeDelegate();        public void Fun()        {            SomeDelegate del = delegate() { Console.WriteLine("我是一个匿名方法,通过委托调用"); };        }    }
匿名方法被定义为内嵌方法,而不是作为任何类的成员,此外无法将方法属性应用到匿名方法,且匿名方法也不能定义一般类型或添加一般约束。如果匿名方法需要参数,则方法签名必须与其指派的委托定义相匹配,如同一个正常方法,例子:

    class SomeClass    {        delegate void SomeDelegate(string str);        public void Fun()        {            SomeDelegate del = delegate(string str)             { Console.WriteLine("我是一个匿名方法,传入了一个参数 "+str); };        }    }
匿名方法还可以直接当作参数传递(委托允许将方法当作参数传递,这个方法可以是匿名方法),例子:
    class MyClass    {        public void MyThread()        {            Thread NewThread = new Thread(delegate() { Console.WriteLine("把方法当作参数传递,开启新线程"); });        }    }
如果忽略delegate关键字后面的空括号,则定义一种特殊的匿名方法,他可以指派给任何委托,不管该委托是什么签名。  例子:
    class MyClass    {        delegate void SomeDelegate(string str);        delegate void SomeDelegate();        public void Fun()        {            SomeDelegate del1 = delegate { Console.WriteLine("delegate关键字后不加括号,则我可以指派给任何委托"); };            SomeDelegate del2 = delegate { Console.WriteLine("delegate关键字后不加括号,则我可以指派给任何委托"); };        }    }
注意一点,委托类型名字可以一样(只要签名不同就不算同一个委托)。

委托还可以定义一般参数”如同泛型“,例子:

    class SomeClass<T>    {        delegate void SomeDelegate<T>(T t);        public void Func(T t)        {            SomeDelegate<int> del = delegate(int num)                                {                                    Console.WriteLine(num);                                };            del(123);        }    }

委托推理:

当将一个方法名指派给委托时,编译器首先推理该委托的类型,然后根据方法名检验他们的签名是否一致,然后会创建这个类型的实例,并把方法添加进调用列表。所以可以直接指派方法名给委托,而不必new一个委托对象,编译器自动为我们做了这些事。

    class Class1    {        delegate void SomeDelegate(ref int i);        public static void Func(ref int i)        {            if (i <= 0) return;            for (int j = 0; j < i; j++)            {                SomeDelegate del = Func;                Console.WriteLine(i);                i--;                del(ref i);            }        }        static void Main(string[] args)        {            int num = 5;            Func(ref num);            Console.ReadKey();        }    }

委托还可以传递方法来回调,如窗体的通信例子:

    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private string str;//主窗体的私有字段        private void Func(string str)//主窗体的私有方法,等下会在新窗体中回调;        {            MessageBox.Show(str);        }        private void button1_Click(object sender, EventArgs e)        {            NewFrm frm = new NewFrm();            frm.str = this.str;//把主窗体的字段传递到新窗体            frm.Del = Func;//相当于授权(传递方法),授权给NewFrm对象的Del变量(委托变量)            frm.Show();        }    }    //新窗体类    public partial class NewFrm : Form    {        public NewFrm()        {            InitializeComponent();        }        public string str;        delegate void SomeDelegate(string str);        public SomeDelegate Del;        private void button1_Click(object sender, EventArgs e)        {            if (Del != null)            {                Del(str);            }        }    }

委托还可以实现多线程,例子:

    class MyClass    {        static void Fun1()        {            for (int i = 0; i < 100000; i++)            {                Console.ForegroundColor = ConsoleColor.Red;                Console.WriteLine(i);            }        }        static void  Fun2()        {            for (int i = 0; i < 10000; i++)            {                Console.ForegroundColor = ConsoleColor.Blue;                Console.WriteLine(i);            }        }        static void Fun3()        {            for (int i = 0; i < 10000; i++)            {                Console.ForegroundColor = ConsoleColor.Yellow;                Console.WriteLine(i);            }        }        static void Main(string[] args)        {            //public Thread(ThreadStart start);这是Thread类定义的构造函数            //public delegate void ThreadStart();ThreadStart其实就是一个委托类型            Thread f1 = new Thread(Fun1);            Thread f2 = new Thread(Fun2);            Thread f3 = new Thread(Fun3);            f1.Start();            f2.Start();            f3.Start();        }    }

2、事件

事件是类在发生其关注的事情时用来提供通知的一种方式”这种通知称为引发事件,引发事件的对象称为事件源,通常可以作为参数传递给方法(当然还可以定义一些其他参数传递过去),事件使用委托来为触发时需要调用的方法提供类型安全的封装,当事件触发时,调用该委托,即调用绑定到委托的方法(事件其实就是委托变量)。例子:

    public partial class MainFrm : Form //主窗体    {        Form1 frm = new Form1();//Form1是主窗体的一个字段,相当于一个Button控件的样子        public MainFrm()        {            frm.Show();            InitializeComponent();            frm.MyEvent += this.Func;//绑定方法        }        public void Func(string str)        { MessageBox.Show(str); }    }    //为Form1添加了一个自定义事件    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();         }        private string str = "我是事件参数";        public delegate void MyDelegate(string str);        public event MyDelegate MyEvent;        private void button1_Click(object sender, EventArgs e)        {            if (MyEvent == null)                return;            MyEvent(str);//当调用button1_Click方法时触发该自定义事件(点击按钮时调用绑定到该事件的方法)        }    }
可以看到,事件其实也是一种方法的传递。触发事件前应先判断是否订阅了事件,订阅事件其实就是绑定方法。在上例中MainFrm订阅了事件。

if (MyEvent == null)                return;//其实就是判断该事件是否订阅了

还可以使用事件访问器声明事件(即添加、移除方法),例子:

    delegate void TestEventDelegate(object o,EventArgs e);    class EventSource    {        private TestEventDelegate TestEventHandlers;        public event TestEventDelegate TestEvent        {            add             {                lock (TestEventHandlers)                {                    TestEventHandlers += value;                }            }            remove            {                lock (TestEventHandlers)                {                    TestEventHandlers -= value;                }            }        }        private void RaiseTestEvent()        {            if (TestEventHandlers != null)            {                TestEventHandlers(this, new System.EventArgs());            }        }    }


---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新生儿不认乳头怎么办 宝宝不吸奶嘴怎么办 孩子不会吸奶瓶怎么办 宝宝突然不吃奶瓶怎么办 换了奶瓶不喝奶怎么办 新生儿不喝奶粉怎么办 7个月小婴儿磨牙怎么办 宝宝出生四天不喝母乳怎么办 我的奶水不足怎么办 乳牙长得不整齐怎么办 新生儿只吃奶粉怎么办 小孩不肯吸母乳怎么办 三个月宝宝不吃奶粉怎么办 宝宝不爱喝水怎么办 崔玉涛 小孩身体铅过高怎么办 疫苗引起的发烧怎么办 婴儿不吃米糊怎么办 宝宝米糊不吃怎么办 换奶瓶宝宝不吃怎么办 小孩不会吃奶瓶怎么办 百天不吃奶瓶怎么办 1岁宝宝积食怎么办 宝宝退烧后流汗怎么办 宝宝高烧后出汗怎么办 发烧出汗不退烧怎么办 婴儿发烧不出汗怎么办 婴幼儿发烧不退怎么办 宝宝突然不吃饭怎么办 宝宝吃饭到处跑怎么办 宝宝不吃奶瓶怎么办崔玉涛 八个月母乳不足怎么办 八个月宝宝厌食怎么办 婴儿辅食便秘怎么办 婴儿被食物卡住怎么办 婴儿食物卡喉咙怎么办 婴儿食物蛋白过敏怎么办 小孩喉咙卡鱼刺怎么办 六个月宝宝腹泻怎么办 断奶后不吃奶粉怎么办 段奶不吃奶粉怎么办 婴儿不肯吃米粉怎么办