我对C#中委托概念的理解

 

 
我们都知道,C#中有“接口”这个概念,所谓的“接口”就是定义一套标准,然后由实现类来具体实现其中的方法,所以说“接口,是一组类的抽象”。同样道理,我们可以将“委托”理解为“方法的抽象”,也就是说定义一个方法的模板,至于这个方法具体是怎么样的,就由方法自己去实现。

我们知道接口的最大好处就是可以实现多态,同理,“委托”是可以实现方法的多态,当我们想调用某个具体方法的时候,我们不直接调用这个方法,而是去调用这个委托。当然,我们必须在具体方法和委托之间建立某种关联。

一、方法的抽象

首先,我们定义一个委托:

public delegate void SaySomething(string name);

这跟抽象方法的语法格式很相似,只是多了一个关键字delegate。既然是对方法的一种抽象,那么我们最关注的当然就是方法的返回值以及方法的参数了。所以上面红色的部分就是我们定义出来的一个规矩,如果某个方法想委托我去做事,那么请你遵循我的规矩,就是返回值为void,参数为一个字符串。我们这个委托的含义是,当某个人来了,就向他说点东西。

好,既然我们已经定义了这个规矩,下面我们就定义具体的方法了。

public void SayHello(string name)
{
    Console.WriteLine("Hello," + name + "!");
}


public void SayNiceToMeetYou(string name)
{
    Console.WriteLine("Nice to meet you," + name + "!");
}

我们这里一共定义了两个方法,一个是向某人说Hello,另一个是向某人说Nice to meet you。我们看到,这里定义的两个方法的返回值和参数跟我们前面定义的“委托”是一致的。

接下来,我们来看事件。

public event SaySomething come;

我们定义了一个事件,这个事件是“有人来了”,注意定义的时候我们使用event关键字,除此之外,我们还加上了前面定义的“委托”的名字。这个意思是说,我这个事件只会跟“SaySomething”打交道,并且,当我这个事件发生的时候,我会通知关注我的这些“委托”(再由这些“委托”去调用具体的方法)。

我们来定义一个测试方法:

public void test() {
    SaySomething sayhello = new SaySomething(SayHello);
    SaySomething saynice = new SaySomething(SayNiceToMeetYou);
    come += sayhello;
    come += saynice;
    come("张三");
}

方法体中的前面两行是用来实例化委托,注意我们用到了new关键字,就好像实例化一个类一样,然后传入一个参数,但这个参数不是string类型、也不是int类型,而是一个方法名。

再下面两行就是将委托加到事件上,意思是说,如果你这个事件发生了,就告诉我一声。可以通过“+=”来将n个委托实例加到某个事件上,一旦这个事件发生,所有的这些委托实例都会得到通知。

最后一行是触发一个事件,注意我们是直接用一个事件名,然后跟一个参数,这又跟“委托”中定义的那个规矩一致(即,要有一个string类型的参数)。

 

 

二、在窗体中委托的应用

上面的例子并不能体现委托和事件的优点,其实,委托和事件在C#中使用非常广泛,例如,当我们点击某个“按钮”的时候,就会有一个“Click”事件触发,而这个事件会通知“委托”,在C#窗体应用程序中,“委托”的名字比较规范,统一使用“EventHandler”,它的具体格式是“void EventHandler(object sender, EventArgs e);”。相信大家都写过下面这样子的HelloWorld程序:


当点击按钮的时候弹出一个对话框。我们怎样实现的呢?你肯定会说,我们在设计窗口双击按钮,就会自动为我们生成类似如下的方法:

private void button1_Click(object sender, EventArgs e)
{
    MessageBox.Show("我被点击了!!!");
}

其实,这里用到的就是事件和委托,这里的button1_Click就是符合EventHandler委托规矩的一个具体的方法,即返回值为void,参数分别是一个object和EventArgs。

我们可以在Form1.Designer.cs中看到如下代码:

this.button1.Click += new System.EventHandler(this.button1_Click);

可以看到,这里有一个Click事件,然后将一个委托实例附加到这个事件上,跟我们前面讲的控制台应用程序中的用法是完全一样的。那这个Click事件是怎么触发的呢?对于这些系统类的事件,并不用我们管。

当然,我们也可以定义自己的事件和委托,例如:A窗体想将某一信息传递给B窗体,那么就可以通过在A窗体中声明一个事件委托来实现。

这里我贴出了我的代码。

A窗体的代码:

private void button1_Click(object sender, EventArgs e)
        {
            string ss = "A窗体向所有调用自己的窗体对象发送了一个问候(包装过的)";
            //1==使用包装后的方法
            ShowInfoChangeToDo(ss);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            string ss = "A窗体向所有调用自己的窗体对象发送了一个问候(直接调用)";
            //2==直接调用
            if (ShowInfoChanged != null)//如果有对象注册
            {
                ShowInfoChanged(this, new ShowInfoEventArgs(ss));//调用所有注册对象的方法
            }
        }

        /// <summary>
        /// 定义一个Delegate,用来规范函数结构。
        /// </summary>
        public delegate void ShowInfoEventHandler(Object sender, ShowInfoEventArgs e);

        /// <summary>
        /// FORM注册delegate事件CultureInfoEventHandler
        /// </summary>
        public event ShowInfoEventHandler ShowInfoChanged;//声明事件

        /// <summary>
        /// virtual 可以用于修饰一个实现的类方法(待重写,override)
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnShowInfoChanged(ShowInfoEventArgs e)
        {
            if (ShowInfoChanged != null)//如果有对象注册
            {
                ShowInfoChanged(this, e);//调用所有注册对象的方法
            }
        }

        /// <summary>
        /// 传递给Observer所感兴趣的信息
        /// </summary>
        public class ShowInfoEventArgs : EventArgs
        {
            private readonly string msg;
            public ShowInfoEventArgs(string _msg)
            {
                msg = _msg;
            }
            public string Msg
            {
                get
                {
                    return this.msg;
                }
            }
        }

        /// <summary>
        /// 一个包装过的方法
        /// </summary>
        public void ShowInfoChangeToDo(string _msg)
        {

            ShowInfoEventArgs e = new ShowInfoEventArgs(_msg);
            OnShowInfoChanged(e);
        }


 

 

B窗体的代码:首先B调用了A,同时也注册了事件

Form1 A = new Form1();
            A.ShowInfoChanged += new Form1.ShowInfoEventHandler(A_ShowInfoChanged);
            A.Show();

void A_ShowInfoChanged(object sender, Form1.ShowInfoEventArgs e)
        {
            MessageBox.Show("来自A窗体的消息>>>>>" + e.Msg);
        }


 

 

当在A窗体上触发某一事件时,就会同时反馈到调用过它的所有对象上(B窗体)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值