【C#】delegate & event Func & Action

delegate

什么是委托?

通俗的来讲,委托除了必须指定delegate关键字和没有方法实体之外,和指定方法没有更多区别。你可以当它是一个占位符,比如你在写代码的时候并不知道你将要处理的是什么。

你只需要知道你将要引入的参数类型和输出类型是什么并定义它即可调用(invoke)委托,相当于调用委托所绑定的方法,一个委托可以绑定多个方法,使用"+="就可以向委托中添加新的方法,

使用"-="可以从委托中删除方法。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象、类型安全的,并且是安全的。

栗子

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();
        }
    }

    class TestDelegate
    {
        public delegate void DelegateMethod();  //声明了一个Delegate Type

        public DelegateMethod delegateMethod;   //声明了一个Delegate对象

        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("---------");
            }
        }
    }

1) 一个 delegate对象一次可以搭载多个方法(methods),而不是一次一个。当我们唤起一个搭载了多个方法(methods)的delegate,所有方法以其“被搭载到delegate对象的顺序”被依次唤起。

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

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

 

Evnet

我们可以把事件编程简单地分成两个部分:事件发生的类(书面上叫事件发生器)和事件接收处理的类。事件发生的类就是说在这个类中触发了一个事件,但这个类并不知道哪个个对象或方法将会加收到并处理它触发的事件。所需要的是在发送方和接收方之间存在一个媒介。这个媒介在.NET Framework中就是委托(delegate)。在事件接收处理的类中,我们需要有一个处理事件的方法。好了,我们就按照这个顺序来实现一个捕获键盘按键的程序,来一步一步说明如何编写事件应用程序。

 

internal class KeyEventArgs : EventArgs
{
    private char keyChar;
    public KeyEventArgs( char keyChar ) : base()
    {
        this.keyChar = keyChar;
    }

    public char KeyChar
    {
        get
        {
            return keyChar;
        }
    }
}

 

引自MSDN:
EventArgs是包含事件数据的类的基类,此类不包含事件数据,在事件引发时不向事件处理程序传递状态信息的事件会使用此类。如果事件处理程序需要状态信息,则应用程序必须从此类派生一个类来保存数据

 

  class Program
    {
        static void Main(string[] args)
        {
            var car = new Car(15);
            new Alerter(car);
            car.Run(120);
        }
    }

    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();
        }
    }

C#中使用事件需要的步骤:

1.创建一个委托

2.将创建的委托与特定事件关联(.Net类库中的很多事件都是已经定制好的,所以他们也就有相应的一个委托,在编写关联事件处理程序--也就是当有事件发生时我们要执行的方法的时候我们需要和这个委托有相同的签名)

3.编写事件处理程序

4.利用编写的事件处理程序生成一个委托实例 5.把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件

C#中事件产生和实现的流程:

1.定义A为产生事件的实例,a为A产生的一个事件

2.定义B为接收事件的实例,b为处理事件的方法

3.A由于用户(程序编写者或程序使用者)或者系统产生一个a事件(例如点击一个Button,产生一个Click事件)

4.A通过事件列表中的委托对象将这个事件通知给B

5.B接到一个事件通知(实际是B.b利用委托来实现事件的接收)

6.调用B.b方法完成事件处理

 

Event事件,是一种封装过的委托。

它拥有以下三要素:

1.事件发行者-达到某些条件时激发事件的对象

2.事件订阅者-订阅事件并对事件发生时进行处理的对象

3.定义发行者和订阅者关系,一个发行者可能会有多个订阅者。

事件和委托的区别

1.委托允许直接通过委托去访问相应的处理函数,而事件只能通过公布的回调函数去调用

2.事件只能通过“+=”,“-=”方式注册和取消订户处理函数,而委托除此之外还可以使用“=”直接赋值处理函数。

 

Func

private delegate string Say();
  public static string SayHello()
        {
            return "Hello";
        }
      static void Main(string[] args)
        {
            Say say = SayHello;
            Console.WriteLine(say());
        }

先声明委托,然后再将方法传给该委托。有没有办法可以不定义委托变量呢?

答案是肯定的,我们可以用Func.

 

Func<TResult>:没有传入参数,返回类型为TResult的委托。就像我们上面的Say委托,就可以用Func<string>来替代,调用如下:

 

 

   static void Main(string[] args)
        {
            Func say = SayHello;
            //Say say = SayHello;
            Console.WriteLine(say());
        }

 

Func<T,?TResult>?委托:有一个传入参数T,返回类型为TResult的委托。如

  //委托 传入参数类型为string,方法返回类型为int
     Func<string, int> a = Count;
      //对应方法
        public int Count(string num)
        {
            return Convert.ToInt32(num);
        }

 

Func<T1,?T2,?TResult>?委托:有两个传入参数:T1T2,返回类型为TResult

类似的还有Func(T1, T2, T3, TResult) 委托、Func(T1, T2, T3, T4, TResult) 委托等。用法差不多,都是前面为方法的传入参数,最后一个为方法的返回类型。

Func也可以与匿名方法,lamda一起使用.

Action

Action<T>?委托:传入参数为T,没有返回类型。如:

 

   static void Main(string[] args)
        {
            Action say = SayHello;
            say("Hello");
        }
        public static void SayHello(string word )
        {
            Console.WriteLine(word);
        }

Action<T1,?T2>?委托:两个传入参数,分别为T1T2,没有返回类型。

其实ActionFunc的用法差不多,差别只是一个有返回类型,一个没有返回类型,当然Action也可以接匿名方法和Lambda表达式。

匿名方法:

复制代码

    static void Main(string[] args)
        {
            Action say = delegate(string word)
            {
                Console.WriteLine(word);
            };
            say("Hello Word");
        }

复制代码
Lambda表达式:

     static void Main(string[] args)
        {
            Action say = s => Console.WriteLine(s);
            say("Hello Word");
        }