`

C#委托事件

    博客分类:
  • C#
阅读更多

Delegate 是在.NET1.0 的时候就已经存在了的特性,因为在准备总结Lambda 表达式的时候发现自己对Delegate 还没有深刻的理解,所以打算先对Delegate 进行整理一下。

       Delegate 中文译为“委托”,MSDN 中对Delegate 是这样进行解释的“C# 中的委托 类似于CC++ 中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内 。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与CC++ 中的函数指针不同,委托是面向对象、类型安全的,并且是安全的 。”

       我们来通过一个Demo 来进行理解:

class Program

    {

        static void OtherClassMethod()

        {

            Console .WriteLine("Delegate an other class's method" );

        }

        static void Main(string [] args)

        {

            var test = new TestDelegate ();

            test.delegateMethod = new TestDelegate .DelegateMethod (test.NonStaticMethod);

             test.delegateMethod += new TestDelegate .DelegateMethod (TestDelegate .StaticMethod);

            test.delegateMethod += Program .OtherClassMethod;

            test.RunDelegateMethods();

            Console .ReadKey();

        }

    }

  class TestDelegate

    {

        // 声明了一个Delegate 类型名为DelegateMethod ,这种Delegate 类型可以搭载返回值为void ,无参函数

        public delegate void DelegateMethod ();

        // 声明了一个Delegate 对象,注意一个是类型,一个是对象

        public DelegateMethod delegateMethod; 

        public static void StaticMethod()

        {

            Console .WriteLine("Delegate a static method" );

        }

        public void NonStaticMethod()

        {

            Console .WriteLine("Delegate a non-static method" );

        }

        public void RunDelegateMethods()

        {

            if (delegateMethod != null )

            {

                Console .WriteLine("---------------------" );

                delegateMethod.Invoke();

                Console .WriteLine("---------------------" );

            }

       }

    }

通过:

   test.delegateMethod = new TestDelegate .DelegateMethod (test.NonStaticMethod);

   test.delegateMethod += new TestDelegate .DelegateMethod (TestDelegate .StaticMethod);

   test.delegateMethod += Program .OtherClassMethod;

 

我们可以发现 delegateMethod 搭载了3 个函数(通过+= 增加搭载的函数,也可以通过-= 来去掉Delegate 中的某个函数),而且通过delegateMethod.Invoke() 运行被搭载的函数,这就是Delegate 可以看作为函数指针的原因。而在Java 中没有委托机制,如果向要实现此功能个人感觉只有通过Java 的内部类来实现。

       既然C# 中的委托与C++ 中函数指针如此相似,那么我简单整理一下Delegate 与函数指针的区别:

1、 一个delegate 对象一次可以搭载多个方法,而不是一次一个。当我们唤起一个搭载多个方法的delegate ,所有方法以开始被搭载到delegate 对象的顺序依次被唤起;

2、 一个delegate 对象所搭载的方法并不需要属于同一个类别,一个delegate 对象所搭载的所有方法必须具有与声明时相同的原型和形式,然而这些方法可以有static 也可以有non-static ,可以由一个或多个不同类别的成员组成;

3、 一个delegate type 的声明在本质上是创建了一个新的subtype instance ,该 subtype 派生自 .NET library framework abstract base classes Delegate MulticastDelegate ,它们提供一组public methods 用以询访delegate 对象或其搭载的方法(methods ) ,与函数指针不同,委托是面向对象、类型安全并且安全的。

 

Delegate 的使用场景:

首先先看下面这段代码

  class Program

    {

        static void Main(string [] args)

        {

            var car = new Car (15);

            new Alerter (car);

            car.Run(120);

            Console .ReadKey();

        }

        class Car

        {

            public delegate void Notify (int value);

             public event Notify notifier;

            private int petrol = 0;

            public int Petrol

            {

                get { return petrol; }

                set

                {

                    petrol = value ;

                    if (petrol < 10)  // petrol 的值小于10 时出发警报

                    {

                        if (notifier != null )

                        {

                            notifier.Invoke(Petrol);

                        }

                    }

                }

            }

            public Car(int petrol)

            {

                Petrol = petrol;

            }

            public void Run(int speed)

            {

                int distance = 0;

                while (Petrol > 0)

                 {

                    Thread .Sleep(500);

                    Petrol--;

                    distance += speed;

                    Console .WriteLine("Car is running... Distance is " + distance.ToString());

                }

            }

        }

        class Alerter

        {

            public Alerter(Car car)

            {

                car.notifier += new Car .Notify (NotEnoughPetrol);

            }

            public void NotEnoughPetrol(int value)

            {

                Console .ForegroundColor = ConsoleColor .Red;

                Console .WriteLine("You only have " + value.ToString() + " gallon petrol left!" );

                Console .ResetColor();

            }

        }

    }

 

通过上面的代码,你可能会有这样的疑问,为什么不再public in Petrol 中直接调用Alerter.NotEnoughtPetrol 呢?因为Car 模块和Alerter 模块本身是两个独立的子系统,如果直接调用,则程序的耦合性就会增加,这是程序员的大忌。

       其实上面的代码是设计模式中的观察者设计模式 的实现,当汽车在运行中汽油量<10 时,就会发出警报,在上面的代码中Delegate 相当于一个存放回调函数的函数指针,使用Delegate ,我们可以非常方便地实现观察者模式。而其实,在需要使用回调函数时,我们都可以考虑使用Delegate

下面我再举个例子吧,取生活中一个场景来说,孩子饿了,要哭,爸爸妈妈听到哭声都会赶过来。如果按照常规的编程方法,我们可能要在Child 类里边实现一个方法来通知爸爸和妈妈,假设有一天这家聚会,爷爷奶奶,姥姥姥爷,姑姑婶婶全过来了,那么孩子必须要通知增加的这些人,我们就不得不修改Child 类里的这个方法。而事实上我们可以这样考虑,将对孩子哭这一事件关心的一类人抽象出来,爷爷奶奶,姥姥姥爷,姑姑婶婶都从该类派生,他们有一个公共的代理,只要他们将自己的行为“注册”到这个代理,孩子一哭,所有被注册进去的事件就会形成一个事件的链表然后顺次执行。在这种模式下Child 类和Observer 类的派生类之间的耦合性大大降低了,我们不需要对Child 类的方法进行任何的修改而只需要讲Observer 类的派生类的各个实例对象对“孩子哭”这一事件的响应注册到“孩子哭”中就可以了。

class Program

    {

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

        class Entry

        {

            public static void Main()

            {

                Woman woman = new Woman ();

                Man man = new Man ();

                Child child = new Child ();

                child.call += new EventHandle (woman.observer_call);

                child.call += new EventHandle (man.observer_call);

                child.observer_call(null , null );

                Console .ReadKey();

            }

        }

        abstract class observer

        {

            public event EventHandle call;

           public void Subto(observer ob)

            {

                this .call += new EventHandle (ob.observer_call);

            }

            abstract public void observer_call(object sender, EventArgs e);

            public void Shout()

            {

                if (call != null )

                {

                    call(this , null );

                }

            }

        }

        class Woman : observer

         {

            public override void observer_call(object sender, EventArgs e)

            {

                Console .WriteLine("Woman : Oh Baby, mom is coming!" );

                Shout();

            }

        }

        class Man : observer

        {

            public override void observer_call(object sender, EventArgs e)

            {

                Console .WriteLine("Man : Oh Baby, papa is coming!" );

                Shout();

            }

        }

        class Child : observer

        {

            public override void observer_call(object sender, EventArgs e)

            {

                Console .WriteLine("Child : Where are my parents? I'm hungry!" );

                Shout();

            }

        }

    }

 

上面我定义了一个委托,它的作用是将被委托的函数以参数形式“传递”给事件,从而构成一个事件的链表。

       说明,委托在编译的时候会编译成类,因为委托是一个类,所以在任何可以声明类的地方都可以声明委托。它定义了方法的类型,使得可以将方法当作另一个方法的参数传递,这种将方法动态的赋给参数的做法,可以避免在程序中大量地使用if else(switch) 语句,同时使得程序具有更好的扩展性;

 

       在上面的“   public event Notify notifier; ”可以改写为“   public Notify notifier; ”这就是我下面需要整理的DelegateEvent

 

Delegate Event

       通过项目发现很多时候DelegateEvent 这两个关键字经常会在一起使用,那两者之间是什么关系呢?通过msdn 的解释是“声明事件:若要在类内声明事件,首先必须声明该事件的委托类型”

  • 大小: 15.3 KB
  • 大小: 5.3 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics