C#语言进阶(二)—事件全解

总目录
C# 语法总目录

事件

事件修饰符: virtual , override , abstract , sealed

解释:

  广播者是声明委托并使用的类,它控制什么时候使用委托。

  订阅者就是给广播类声明的委托,使用 += 或 -= 改变委托数量的这一操作过程者。

用法:声明事件就是在声明委托时前面加上 event 关键字。

作用:事件的作用是 让委托的广播者和订阅者的 结构更清晰。

过程:编译器看到这个委托添加了事件关键字,会自动让委托变成私有的,然后给它添加对应的访问器类似类属性的{set;get;},这样订阅者就只能通过访问器来修改自己的委托。

  所以,简单来说,事件就是把委托默认设置为私有,然后创建一个默认的访问器让外部只能通过访问器+=或-=修改委托的成员,且不能将这个委托置null。

1.基本用法 发布订阅模式

#region 普通的  发布订阅模式
public delegate void ScoreChangeHandle(decimal oldScore, decimal newScore);
//广播者
internal class BroadCaster
{
    
    
    private string? name;
    private decimal score;

    public event ScoreChangeHandle? ScoreChangedEvent;

    public BroadCaster(string name)
    {
    
    
        this.name = name;
    }
    public decimal Score
    {
    
    
        get {
    
     return score;}
        set
        {
    
    
            if (score == value) return;
            decimal oldScore = score;
            score = value;
            ScoreChangedEvent?.Invoke(oldScore, score);
        }
    }
}
//订阅者
internal class Subscriber
{
    
    
    private readonly string _id;
    public Subscriber(string id, BroadCaster broad)
    {
    
    
        _id = id;
        broad.ScoreChangedEvent += ScoreChange;
    }

    void ScoreChange(decimal oldScore, decimal newScore)
    {
    
    
        Console.WriteLine("this id is: "+_id+",  oldscore is " + oldScore + "  ,new Score is: " + newScore+"  ,time is: "+ DateTime.Now);
    }
}
#endregion

static void Main(string[] args)
{
    
    
    BroadCaster bc = new BroadCaster("bc1");
    Subscriber sub1 = new Subscriber("01", bc);
    Subscriber sub2 = new Subscriber("02", bc);
    bc.Score = 10;
}

//输出
this id is: 01,  oldscore is 0  ,new Score is: 10  ,time is: 2024/4/18 16:19:33
this id is: 02,  oldscore is 0  ,new Score is: 10  ,time is: 2024/4/18 16:19:33

2. .net标准事件模型

  标准事件模型是 .net framwork 定义的一个标准。可用可不用,只是一个标准而已。

  官方为这个标准定义了一个事件参数类,用于给事件传递参数。这就是上面说的,这个模型可用可不用,不用官方的,自己也能做一个类似的,做个开发,没必要搞得这么复杂。

  以下是上面案例根据标准事件模型的修改版本。

  这里使用 .net framwork的标准事件模型参数类: System.EventArgs 类,来模拟标准事件模型

#region .net Framework 标准事件模型
//继承标准事件模型参数类型
//这个父类啥都没有,只有一个静态参数,一个构造方法,可以点进去看
public class ScoreChangedEventArgs : EventArgs
{
    
    
    public static readonly new ScoreChangedEventArgs? Empty;
    //通常标准事件模型传递的参数设置为只读类型
    public readonly decimal oldScore;
    public readonly decimal newScore;
    public ScoreChangedEventArgs(decimal oldScore,decimal newScore)
    {
    
    
        this.oldScore = oldScore;
        this.newScore = newScore;
    }
}
//发布者
public class BroadCasterStandar
{
    
    
    private string? name;
    private decimal score;
    //事件标准委托
    public event EventHandler<ScoreChangedEventArgs>? ScoreChanged;

    protected virtual void OnScoreChanged(ScoreChangedEventArgs? e)
    {
    
    
        ScoreChanged?.Invoke(this, e);
    }

    public BroadCasterStandar(string name)
    {
    
    
        this.name = name;
    }
    public decimal Score
    {
    
    
        get {
    
     return score; }
        set
        {
    
    
            if (score == value) return;
            decimal oldScore = score;
            score = value;
            
            OnScoreChanged(new ScoreChangedEventArgs(oldScore, score));
            //如果不需要传值,那么可以用下面代替
            //OnScoreChanged(ScoreChangedEventArgs.Empty);
        }
    }
}
//订阅者
internal class SubscriberStandar
{
    
    
    private readonly string _id;
    public SubscriberStandar(string id, BroadCasterStandar broad)
    {
    
    
        _id = id;
        //订阅信息
        broad.ScoreChanged += ScoreChanged;
    }

    //处理广播信息
    void ScoreChanged(object? obj, ScoreChangedEventArgs e)
    {
    
    
        if (e == ScoreChangedEventArgs.Empty)
        {
    
    
            return;
        }
        Console.WriteLine("this id is: " + _id + ",  oldscore is " + e.oldScore + "  ,new Score is: " + e.newScore + "  ,time is: " + DateTime.Now);
    }
}
#endregion
    
static void Main(string[] args)
{
    
    
    BroadCasterStandar bcs = new BroadCasterStandar("bcs");
    SubscriberStandar sbs1 = new SubscriberStandar("01", bcs);
    SubscriberStandar sbs2 = new SubscriberStandar("02", bcs);
    //广播信息
    bcs.Score = 15;
}

//输出
this id is: 01,  oldscore is 0  ,new Score is: 15  ,time is: 2024/4/18 16:43:12
this id is: 02,  oldscore is 0  ,new Score is: 15  ,time is: 2024/4/18 16:43:12

3. 事件访问器

  默认情况下,编译器会默认实现事件的访问器,如果显示的去实现,那么编译器就不会自动取生成默认的访问器。

  从功能上,自己手动写访问器和编译器默认生成是一样的,但是编译器默认生成的使用了无锁的比较并交换算法,保证在更新委托时的线程安全性,在多线程情况下更安全些。

  一般情况不需要自己去显示写事件访问器,如果需要更多高级操作,那么就不得不用。

  以下是一些可能需要用的情况 (当然也可以不用):

  • 当事件很多,订阅者却不多,这种情况下需要自定义访问器的add方法,在方法里使用字典来存储委托引用,这样比原来的开销小
  • 当广播类继承了多个事件接口,并且有多个接口的事件名称是相同的,那么需要把这些接口的事件显示的去创建访问器,用来区分它们用哪个访问器访问
#region 继承多个接口 显示创建访问器
public interface IMathScore
{
    
    
    event EventHandler<ScoreChangedCusEventArgs> ScoreChanged;
}
public interface IEnglishScore
{
    
    
    event EventHandler<ScoreChangedCusEventArgs> ScoreChanged;
}

public class ScoreChangedCusEventArgs : EventArgs
{
    
    
    public static readonly new ScoreChangedCusEventArgs? Empty;
    //通常标准事件模型传递的参数设置为只读类型
    public readonly decimal oldScore;
    public readonly decimal newScore;
    public ScoreChangedCusEventArgs(decimal oldScore, decimal newScore)
    {
    
    
        this.oldScore = oldScore;
        this.newScore = newScore;
    }
}
//发布者
public class BroadCasterCusStandar:IMathScore,IEnglishScore
{
    
    
    private string? name;
    private decimal englishScore;
    private decimal mathScore;

    event EventHandler<ScoreChangedCusEventArgs> MathEvent;
    event EventHandler<ScoreChangedCusEventArgs> EnglishEvent;

    object objectLock = new Object();

    event EventHandler<ScoreChangedCusEventArgs>? IMathScore.ScoreChanged
    {
    
    
        add {
    
    
            lock (objectLock)
            {
    
    
                MathEvent += value;
            }
        }
        remove {
    
    
            lock (objectLock)
            {
    
    
                MathEvent -= value;
            }
        }
    }

    event EventHandler<ScoreChangedCusEventArgs>? IEnglishScore.ScoreChanged
    {
    
    
        add
        {
    
    
            lock (objectLock)
            {
    
    
                EnglishEvent += value;
            }
        }
        remove
        {
    
    
            lock (objectLock)
            {
    
    
                EnglishEvent -= value;
            }
        }
    }

    protected virtual void OnMathScoreChanged(ScoreChangedCusEventArgs? e)
    {
    
    
        MathEvent?.Invoke(this, e);
    }
    protected virtual void OnEnglishScoreChanged(ScoreChangedCusEventArgs? e)
    {
    
    
        EnglishEvent?.Invoke(this, e);
    }

    public BroadCasterCusStandar(string name)
    {
    
    
        this.name = name;
    }
    public decimal MathScore
    {
    
    
        get {
    
     return mathScore; }
        set
        {
    
    
            if (mathScore == value) return;
            decimal oldMathScore = mathScore;
            mathScore = value;

            OnMathScoreChanged(new ScoreChangedCusEventArgs(oldMathScore, mathScore));
            //如果不需要传值,那么可以用下面代替
            //OnMathScoreChanged(ScoreChangedCusEventArgs.Empty);
        }
    }

    public decimal EnglishScore
    {
    
    
        get {
    
     return englishScore; }
        set
        {
    
    
            if (englishScore == value) return;
            decimal oldEnglishScore = englishScore;
            englishScore = value;

            OnEnglishScoreChanged(new ScoreChangedCusEventArgs(oldEnglishScore, englishScore));
            //如果不需要传值,那么可以用下面代替
            //OnEnglishScoreChanged(ScoreChangedCusEventArgs.Empty);
        }
    }
}
//订阅者
internal class SubscriberCus1Standar
{
    
    
    private readonly string _id;
    public SubscriberCus1Standar(string id, BroadCasterCusStandar broad)
    {
    
    
        _id = id;
        IEnglishScore englishBroad = (IEnglishScore)broad;
        //订阅信息
        englishBroad.ScoreChanged += ScoreChanged;
    }

    //处理广播信息
    void ScoreChanged(object? obj, ScoreChangedCusEventArgs e)
    {
    
    
        if (e == ScoreChangedCusEventArgs.Empty)
        {
    
    
            return;
        }
        Console.WriteLine("this id is: " + _id + ",  oldscore is " + e.oldScore + "  ,new Score is: " + e.newScore + "  ,time is: " + DateTime.Now);
    }
}
internal class SubscriberCus2Standar
{
    
    
    private readonly string _id;
    public SubscriberCus2Standar(string id, BroadCasterCusStandar broad)
    {
    
    
        _id = id;
        IMathScore englishBroad = (IMathScore)broad;
        //订阅信息
        englishBroad.ScoreChanged += ScoreChanged;
    }

    //处理广播信息
    void ScoreChanged(object? obj, ScoreChangedCusEventArgs e)
    {
    
    
        if (e == ScoreChangedCusEventArgs.Empty)
        {
    
    
            return;
        }
        Console.WriteLine("this id is: " + _id + ",  oldscore is " + e.oldScore + "  ,new Score is: " + e.newScore + "  ,time is: " + DateTime.Now);
    }
}
#endregion
    
//输出
this id is: 02,  oldscore is 0  ,new Score is: 15  ,time is: 2024/4/18 17:34:35
this id is: 01,  oldscore is 0  ,new Score is: 20  ,time is: 2024/4/18 17:34:35 

总目录
C# 语法总目录

猜你喜欢

转载自blog.csdn.net/qq_44653106/article/details/139813562