C#事件(Event)学习日记

event 关键字的来由,为了简化自定义方法的构建来为委托调用列表增长和删除方法。html

在编译器处理 event 关键字的时候,它会自动提供注册和注销方法以及任何须要的委托类型成员变量。架构

这些委托成员变量老是声明为私有的,所以不能直接从触发事件对象访问它们。框架

 

舒适提示:若是您对于委托不是很了解,您能够先看 C#委托(Delegate) ,这对您理解本章会有所帮助。函数

定义一个事件的步骤:学习

  1. 须要定义一个委托,它包含事件触发时将要调用方法
  2. 经过 event 关键字用相关委托声明这个事件

话很少说,咱们来看一个示例:this

 

1. 定义Car类:spa

public class Car
{
    // 这个委托用来与Car事件协做
    public delegate void CarEngineHandler(string msg);

  //
这种汽车能够发送这些事件 public event CarEngineHandler Exploded; public event CarEngineHandler AboutToBlow; public int CurrentSpeed { get; set; } public int MaxSpeed { get; set; } public string PetName { get; set; } private bool CarIsDead; public Car() { MaxSpeed = 100; } public Car(string name, int maxSp, int currSp) { CurrentSpeed = currSp; MaxSpeed = maxSp; PetName = name; } public void Accelerate(int delta) { // 若是Car没法使用了,触发Exploded事件 if (CarIsDead) { if (Exploded != null) { Exploded("sorry,this car is dead"); } } else { CurrentSpeed += delta; // 确认已没法使用,触发AboutToBlow事件 if ((MaxSpeed - CurrentSpeed) == 10 && AboutToBlow != null) { AboutToBlow("careful buddy ! gonna blow !"); } if (CurrentSpeed >= MaxSpeed) { CarIsDead = true; } else { Console.WriteLine($"CurrentSpeed={CurrentSpeed}"); } } } }

 以上咱们已经设定了Car对象发送两个自定义事件,这再也不须要自定义注册函数,也不须要声明委托成员变量。稍后咱们将说到如何使用这个汽车,在此以前,让咱们了解一下事件的架构,揭开事件的神秘面纱。3d

 

2. 事件神秘面纱code

C#事件事实上会扩展两个隐藏的公共方法,一个 add_事件名称,一个 remove_事件名称。htm

 

add_Exploded() CIL指令

 

remove_Exploded() CIL指令

 

表明事件自己的CIL代码使用 .addon 和 .removeon 指令调用对应的 add_xxx() 和 remove_xxx()方法

 

3. 使用Car类

了解了这些以后,咱们来使用以前定义的Car类:

public class MyEvent
{
    public static void Show()
    {
        WriteLine("fun with events");
        Car c1 = new Car("bwm", 100, 10);

        // 注册事件处理程序
        c1.AboutToBlow += new Car.CarEngineHandler(CarIsAlomostDoomed);
        c1.AboutToBlow += new Car.CarEngineHandler(CarAboutToBlow);

        Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded);
        c1.Exploded += d;

        WriteLine("******Speeding up******");
        for (int i = 0; i < 6; i++)
        {
            c1.Accelerate(20);
        }

        // 注销,从调用列表中移除CarExploded()方法
        c1.Exploded -= d;

        WriteLine("******Speeding up******");
        for (int i = 0; i < 6; i++)
        {
            c1.Accelerate(20);
        }
    }
    private static void CarExploded(string msg) => WriteLine($"CarExploded-> {msg}");

    private static void CarAboutToBlow(string msg) => WriteLine($"CarAboutToBlow=>{msg}");

    private static void CarIsAlomostDoomed(string msg) => WriteLine($"CarIsAlomostDoomed-> {msg}");
}

 

运行效果图:

 

为了进一步简化事件注册,咱们能够用到委托章节学习到的方法组转换语法(解释:我能够在调用以委托做为参数的方法时,直接提供方法的名称,而不是委托对象)

下面请看使用方法组转换,注册和注销事件,粗体部分:

public static void Show()
{
    WriteLine("fun with events");
    Car c1 = new Car("bwm", 100, 10);

    // 注册事件处理程序
    c1.AboutToBlow += CarIsAlomostDoomed; c1.AboutToBlow += CarAboutToBlow; c1.Exploded += CarExploded; 
    WriteLine("******Speeding up******");
    for (int i = 0; i < 6; i++)
    {
        c1.Accelerate(20);
    }
// 注销,从调用列表中移除CarExploded()方法 c1.Exploded -= CarExploded; WriteLine("******Speeding up******"); for (int i = 0; i < 6; i++) { c1.Accelerate(20); } }

 

4. 建立自定义事件参数

微软的事件模式:(System.Object sender,System.EventArgs args)这一两个参数的模型。

第一个参数 sender :表示一个对发送事件的对象(Car)的引用,

第二个参数 args :与该事件相关的信息

System.EventArgs 基类源代码:

public class EventArgs {
    public static readonly EventArgs Empty = new EventArgs();
    
    public EventArgs() 
    {
    }
}

那么对于简单的事件类型来讲,咱们能够直接传递一个EventArgs的实例,可是若是咱们指望传递自定义的数据,就应该从System.EventArgs派生出一个子类。
咱们接下来就为咱们的 Car 自定义一个符合这种事件模式的事件参数,新建一个 CarEventArgs 类,包含一个字符串,表示要发送给接收者的信息:

public class CarEventArgs : EventArgs
{
    public readonly string msg;
    public CarEventArgs(string message)
    {
        msg = message;
    }
}

 

咱们修改一下Car类,新添加一个 CarCustomEngineHandler 委托,而且更改相应的事件代码:

public class Car
{
    public delegate void CarCustomEngineHandler(object sender, CarEventArgs e);

    // 模仿微软正规(object sender, EventArgs e)写法
    public event CarCustomEngineHandler CustomExploded;
    public event CarCustomEngineHandler CustomAboutToBlow;

    public void AccelerateCustom(int delta)
    {
        if (CarIsDead)
        {
            if (CustomExploded != null)
            {
                CustomExploded(this, new CarEventArgs("sorry,this car is dead"));
            }
        }
        else
        {
            CurrentSpeed += delta;

            if ((MaxSpeed - CurrentSpeed) == 10 && CustomAboutToBlow != null)
            {
                CustomAboutToBlow(this, new CarEventArgs("careful buddy ! gonna blow !"));
            }

            if (CurrentSpeed >= MaxSpeed)
            {
                CarIsDead = true;
            }
            else
            {
                Console.WriteLine($"CurrentSpeed={CurrentSpeed}");
            }
        }
    }
}

 

看一下调用粗体部分(是如何使用传递的参数sender,e的):

public class MyCustomEvents
{
    public static void Show()
    {
        WriteLine("fun with events");
        Car c1 = new Car("bwm", 100, 10);

        c1.CustomAboutToBlow += CarIsAlomostDoomed;
        c1.CustomAboutToBlow += CarAboutToBlow;

        Car.CarCustomEngineHandler d = CarExploded;
        c1.CustomExploded += d;

        WriteLine("******Speeding up******");
        for (int i = 0; i < 6; i++)
        {
            c1.AccelerateCustom(20);
        }

        c1.CustomExploded -= d;

        WriteLine("******Speeding up******");
        for (int i = 0; i < 6; i++)
        {
            c1.AccelerateCustom(20);
        }
    }

    private static void CarExploded(object sender, CarEventArgs e) => WriteLine($"CarExploded->{((Car)sender)?.PetName} {e.msg}"); private static void CarAboutToBlow(object sender, CarEventArgs e) => WriteLine($"CarAboutToBlow=>{((Car)sender)?.PetName} {e.msg}"); private static void CarIsAlomostDoomed(object sender, CarEventArgs e) => WriteLine($"CarIsAlomostDoomed->{((Car)sender)?.PetName} {e.msg}"); }


5. 泛型 EventHandler<T> 委托

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

因为不少自定义委托接受(object,EventArgs)这样的参数结构,那么咱们能够使用框架内置的 EventHandler<> 来简化咱们的事件 委托。

 

首先修改一下Car类:

public class Car
{
    public event EventHandler<CarEventArgs> StandardExploded;
    public event EventHandler<CarEventArgs> StandardAboutToBlow;

    public void AccelerateStandard(int delta)
    {
        if (CarIsDead)
        {
            if (StandardExploded != null)
            {
                StandardExploded(this, new CarEventArgs("sorry,this car is dead"));
            }
        }
        else
        {
            CurrentSpeed += delta;

            if ((MaxSpeed - CurrentSpeed) == 10 && StandardAboutToBlow != null)
            {
                StandardAboutToBlow(this, new CarEventArgs("careful buddy ! gonna blow !"));
            }

            if (CurrentSpeed >= MaxSpeed)
            {
                CarIsDead = true;
            }
            else
            {
                Console.WriteLine($"CurrentSpeed={CurrentSpeed}");
            }
        }
    }
}


调用代码其实和上一段并无太大差别,这里仍是贴出来:

public class MyStandardEvent
{
    public static void Show()
    {
        WriteLine("fun with events");
        Car c1 = new Car("bwm", 100, 10);

        c1.StandardAboutToBlow += CarIsAlomostDoomed;
        c1.StandardAboutToBlow += CarAboutToBlow;

        EventHandler<CarEventArgs> d = CarExploded;         c1.StandardExploded += d;

        WriteLine("******Speeding up******");
        for (int i = 0; i < 6; i++)
        {
            c1.AccelerateStandard(20);
        }

        c1.StandardExploded -= d;

        WriteLine("******Speeding up******");
        for (int i = 0; i < 6; i++)
        {
            c1.AccelerateStandard(20);
        }
    }

    private static void CarExploded(object sender, CarEventArgs e) => WriteLine($"CarExploded->{((Car)sender)?.PetName} {e.msg}");

    private static void CarAboutToBlow(object sender, CarEventArgs e) => WriteLine($"CarAboutToBlow=>{((Car)sender)?.PetName} {e.msg}");

    private static void CarIsAlomostDoomed(object sender, CarEventArgs e) => WriteLine($"CarIsAlomostDoomed->{((Car)sender)?.PetName} {e.msg}");
}

 

 

6.匿名方法

这么简单的处理操做, CarExploded() ,CarAboutToBlow()这一的方法不多会被调用委托以外的任何程序所调用。从生成效率来讲,手工定义一个由委托对象调用的方法有点麻烦耶。

为了解决这种状况,如今事件注册时,能够直接将一个委托与一段代码关联 -- 匿名方法

咱们修改一下调用Car类的地方(注意粗体部分、最后一个大括号 ";" 结束):

public class MyAnonymousMtehoden
{
    public static void Show()
    {
        int aboutToBlowCounter = 0;

        WriteLine("fun with events");
        Car c1 = new Car("bwm", 100, 10);

        c1.StandardAboutToBlow += delegate
        {
            WriteLine("Eek,going to fast");
        };

        c1.StandardAboutToBlow += delegate (object sender, CarEventArgs e)
        {
            aboutToBlowCounter++;
            WriteLine($"CarAboutToBlow=>{((Car)sender)?.PetName} {e.msg}");
        };

        c1.StandardExploded += delegate (object sender, CarEventArgs e)
        {
            aboutToBlowCounter++;

            WriteLine($"Exploded=>{((Car)sender)?.PetName} {e.msg}");
        }; for (int i = 0; i < 6; i++)
        {
            c1.AccelerateStandard(20);
        }

        WriteLine($"aboutToBlowCounter={aboutToBlowCounter}");
    }
}

 

本文参考《精通C#》

 

学无止境,望各位看官多多指教。

相关文章
相关标签/搜索