C#的委托

1.       委托的定义

委托声明定义了一种类型,它用一组特定的参数以及返回类型来封装方法。对于静态方法,委托对象封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果您有一个委托对象和一组适当的参数,则可以用这些参数调用该委托。

委托是一种安全地封装方法的类型,它与 C C++ 中的函数指针类似。与 C 中的函数指针不同,委托是面向对象的、类型安全的和保险的。委托的类型由委托的名称定义。

下面的示例声明了一个名为 Del 的委托,该委托可以封装一个采用字符串作为参数并返回 void 的方法

public delegate void Del (string message);

2. 使用委托

下面举个简单的例子

public class PrinStr

{

    //声明一个输出的方法

    public void CallPrint(string InPut)

    {

        Console.WriteLine(InPut);

    }

}

 

class ListMembers

{

    //声明委托类型

    public delegate void PrintHandler(string str);

 

    public static void Main (String[] args)

    {

        PrinStr clsPS = new PrinStr();

 

        //clsPS的方法实例化委托

        PrintHandler myHandler = new PrintHandler(clsPS.CallPrint);

 

        //调用委托的方法

        myHandler("Hellp World!");        

 

}

使用委托的注意点:

 1.创建委托所使用的方法必须和委托声明相一致(参数列表、返回值都一致)

 2.利用 +=-=来进行委托的链接、取消链接或直接使用Delegate.CombineDelegate.Remove方法来实现

3.可以使用MulticastDelegate的实例方法GetInvocationList()来获取委托链中所有的委托

 4.不能撰写包含 out 参数的委托

可能这时会有疑问了,使用委托跟直接调用有什么区别?

那我们再来看一个例子

有一个公司(场景),你是老板,手下有主管和员工,作为老板你会指派(委托)主管管理员工的工作,如果某个员工玩游戏,则让某个主管从该员工的薪水里扣去500元钱。

  这就是现实中的委托。

而在写程序中,假设程序员就是老板,有两个类分别为主管和员工,而主管小王和员工小张就是两个类的对象实例。员工类有一个方法:玩游戏,同时就有一个玩游戏的事件,他一玩游戏就会激发这个事件。而主管类就是负责处理该事件的,他负责把玩游戏的员工的薪水扣除500

2.1 首先,我们来看看在非委托的情况下比较常见的一种设计方式(当然这不是唯一的方式,也不是最好的方式,但是很常见):

using System;

namespace TestDelegate

{

    public class 场景

    {

        [STAThread]

        public static void Main (string[] args)

        {

            Console.WriteLine("场景开始了.");

            // 生成主管类的对象实例小王

            主管 小王= new 主管();

            // 生成员工类的对象实例小张,指定他的主管

            员工 小张= new 员工(小王);

            Console.WriteLine("该员工本有的薪水:" + 小张.薪水.ToString());

            // 员工开始玩游戏

            小张.玩游戏();

            Console.WriteLine("现在该员工还剩下:" + 小张.薪水.ToString());

            Console.WriteLine("场景结束");

            Console.ReadLine();

        }

    }

    // 负责扣钱的人----主管

    public class 主管

    {

        public 主管()

        {

            Console.WriteLine("生成主管");

        }

        public void 扣薪水(员工 employee)

        {

            Console.WriteLine("主管:好小子,上班时间胆敢玩游戏");

            Console.WriteLine("主管:看看你小子有多少薪水");

            Console.WriteLine("开始扣薪水...");

            System.Threading.Thread.Sleep(1000);

            employee.薪水= employee.薪水- 500;

            Console.WriteLine("扣薪水执行完毕.");

        }

    }

    // 如果玩游戏,则会引发事件

    public class 员工

    {

        // 保存员工的薪水

        private int m_Money;

        // 保存该员工的主管

        private 主管 m_Manager;

        public 员工(主管 manager)

        {

            Console.WriteLine("生成员工.");

            m_Manager = manager;  // 通过构造函数,初始化员工的主管。

            m_Money = 1000; // 通过构造函数,初始化员工的薪水。

        }

        public int 薪水// 此属性可以操作员工的薪水。

        {

            get

            {

                return m_Money;

            }

            set

            {

                m_Money = value;

            }

        }

        public void 玩游戏()

        {

            Console.WriteLine("员工开始玩游戏了..");

            Console.WriteLine("员工:CS真好玩,哈哈哈!我玩...");

            System.Threading.Thread.Sleep(1000);

            m_Manager.扣薪水(this);

        }

    }

}

  这种方法所带来的问题: 员工类和主管类的耦合性太高

  1   在客户程序里必须先创建了主管类之后才能生成员工类,如果在不需要主管类对象而只需员工类对象的地方,为了创建所需的员工类对象实例,你也不得不去先创建一个主管类的对象实例;

  2   如果场景剧本(即客户程序需求)发生了变化

  (1)、现在要让一个新的角色(一个新的类),如保安,来代替主管,负责在员工玩游戏时扣员工薪水,那么我们不得不去修改员工类,或许还需要修改主管类;

  (2)、如果场景剧本增加新的需求,要求员工在玩游戏后,不但要扣薪水,还要在绩效上扣分,那么我们也不得不修改员工类。

2.2 使用委托实现                 

下面例子在控制台应用程序运行成功

namespace TestDelegate

{

    //声明委托

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

 

    //定义主管类

    public class 主管

    {

        public 主管()

        {

            Console.WriteLine("生成主管!");

        }

 

        public void 扣薪水(object sender,EventArgs e)

        {

            Console.WriteLine("主管:好小子,上班时间胆敢玩游戏");

            Console.WriteLine("主管:看看你小子有多少薪水");

            员工 employee = (员工)sender;

 

            Console.WriteLine("开始扣薪水...");

 

            System.Threading.Thread.Sleep(1000);

            employee.薪水= employee.薪水- 500;

 

            Console.WriteLine("扣薪水执行完毕.");

 

        }

    }

 

    public class 员工

    {

        //定义一个事件,表示员工正在玩游戏

        public event PlayGameHandler playgame;

 

        //员工的薪水

        private int m_Money;

 

        public 员工()

        {

            Console.WriteLine("生成员工!");

            m_Money = 1000;

        }

 

        //定义薪水的属性

        public int 薪水

        {

            get { return m_Money; }

            set { m_Money = value; }

        }

 

        public void 玩游戏()

        {

            Console.WriteLine("员工开始玩游戏了..");

            Console.WriteLine("员工:CS真好玩,哈哈哈!我玩...");

 

            //线程休眠秒

            System.Threading.Thread.Sleep(1000);

 

            EventArgs e = new EventArgs();

            OnPlayGame(e);

        }

 

        public virtual void OnPlayGame(EventArgs e)

        {

            if (playgame != null)

            {

                playgame(this, e);

            }

        }

    }

 

    public class Test

    {

        public static void Main (string[] args)

        {

            Console.WriteLine("场景开始!");

 

            // 生成主管类的对象实例小王

         主管 小王= new 主管();

 

         // 生成员工类的对象实例小张

         员工 小张= new 员工();

 

         // 设下委托,指定监视

           小张.playgame += new PlayGameHandler(小王.扣薪水);

           Console.WriteLine("该员工本有的薪水:" + 小张.薪水.ToString());

 

           // 员工开始玩游戏

           小张.玩游戏();

           Console.WriteLine("现在该员工还剩下:" + 小张.薪水.ToString());

           Console.WriteLine("场景结束");

        }

}

}

对于前面提出的问题:

1)   解决了主管类和员工类的必然联系,可以单独创建员工对象实例,而不用管是否有主管类对象实例的存在

2)   在客户程序变化时,我们只需修改客户程序,如上面的例子

a) 保安 小李 = new 保安();

  小张.PlayGame += new PlayGameHandler(小李. 扣薪水);

可实现由保安来负责扣薪水的需求变化,而不用动员工类

b)小张.PlayGame += new PlayGameHandler(某某. 扣绩效分);

  这个某某可以是主管,也可以是其他新的角色(新的类),只需要在某某对应的类里定义扣绩效分的动作即可,而不用动员工类。

 

当前,不使用委托我们依然可以实现解耦的类,只不过要多写很多类和接口,但这样不仅是增加了代码的复杂性和逻辑性,维护起来也很麻烦。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值