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

---------------------- 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培训、期待与您交流! ----------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值