C#复习笔记-事件

使用委托时,一般会出现两种角色,广播者和订阅者。广播者是包含委托字段的类型,它通过委托决定何时进行广播。订阅者是方法的接收者。它通过在广播者的委托上调用+=或者-=来决定何时开始监听何时结束监听。事件是一种使用委托的功能实现广播者或订阅者模型的结构。使用委托的主要目的是保证订阅者之间互不影响。声明事件的最简单的方法是在委托成员前面加上event关键字:

//定义一个委托,接受2个decimal类型的参数
    public delegate void PirceChangedHandler(decimal oldPirce, decimal newPrice);

    public class Broadcaster
    {
        //定一个PirceChanged事件
        public event PirceChangedHandler PriceChanged;
        decimal _price;

        public decimal Price
        {
            get { return _price; }
            set
            {
                if (_price == value)
                    return;
                decimal oldprice = _price;
                _price = value;
                if (PriceChanged != null)
                    PriceChanged(oldprice, _price);
            }
        }
    }

EventArgs是为事件传递信息的基类。我们可以继承EventArgs以便于在事件触发的时候传递值参数。EventArgs子类应当根据它包含的信息来命名,而非根据使用它的事件命名。一般将数据以属性或制度字段的方式暴露给外界。

public class PriceChangedEventArgs : EventArgs
{
    public readonly decimal Price;
    public readonly decimal OldPrice;

    public PriceChangedEventArgs(decimal price, decimal oldPrice)
    {
        Price = price;
        OldPrice = oldPrice;
    }
}

 定义了EvaenArgs子类以后,下一步就要定义事件的委托了,委托必须满足下面三个条件:

  • 委托必须以void作为返回值。
  • 委托必须接受两个参数。第一个参数是object类型,第二个参数是EventArgs子类。第一个参数表明了时间的广播者,第二个参数包含了需要传递的信息。
  • 委托的名称必须以EventHandler结尾。如下:
public delegate void PriceChangedEventHandler(object sender, PriceChangedEventArgs e);

框架定义了一个名为System.EventHandler<>的泛型委托也满足以上三个条件,事件模式还需要编写一个protected的虚方法来触发事件,方法名和时间名一致,以On为前缀,接收唯一的EvenArgs参数:

    public class Stock
    {
        public event EventHandler<PriceChangedEventArgs> PriceChanged;
        protected virtual void OnPriceChanged(PriceChangedEventArgs e)
        {
            var temp = PriceChanged;//为了多线程下可靠的工作,在测试和调用委托之前需要将他保存到零时变量
            if (temp != null)
                temp(this, e);
            PriceChanged?.Invoke(this, e);//也可以使用null条件运算符来避免声明临时变量,这种写法是最好的事件触发方式
        }
    }

  完整的代码如下:

    public class PriceChangedEventArgs : System.EventArgs
    {
        public readonly decimal LastPrice;
        public readonly decimal NewPrice;

        public PriceChangedEventArgs(decimal lastPrice, decimal newPrice)
        {
            LastPrice = lastPrice;
            NewPrice = newPrice;
        }
    }
    public class Stock
    {
        public event EventHandler<PriceChangedEventArgs> PriceChanged;
        protected virtual void OnPriceChanged(PriceChangedEventArgs e)
        {
            //为了多线程下可靠的工作,调用委托之前需要将他保存到零时变量
            //var temp = PriceChanged;
            //if (temp != null)
            //    temp(this, e);
            PriceChanged?.Invoke(this, e);//也可以这么写,线程安全又书写简单,是最好的触发方式。
        }

        decimal price;
        public decimal Price
        {
            get { return price; }
            set
            {
                if (price == value)
                    return;
                decimal priceold = price;
                price = value;
                OnPriceChanged(new PriceChangedEventArgs(priceold, price));
            }
        }
    }

using App;

Stock stock = new Stock();
stock.Price = 20;
stock.PriceChanged += Stock_PriceChanged;

for (int i = 0; i < 10; i++)
{
    stock.Price = i + 20;
    Task.Delay(1000).Wait();
}

void Stock_PriceChanged(object? sender, PriceChangedEventArgs e)
{
    Console.WriteLine($"当前价格:{e.NewPrice},上一次的价格{e.LastPrice}");
}
Console.ReadKey();

 

和方法一样,事件也可以重写,可以是虚的,抽象的密封的,静态的。

 

posted @ 2023-04-05 23:02  放羊娃  阅读(16)  评论(0编辑  收藏  举报