C# 事件

前言

本文只是为了复习,巩固,和方便查阅,一些知识点的详细知识会通过相关链接和文献标记出来。

为什么要用事件

要考虑这个问题之前,首先要知道使用委托的缺点,先看下面几段代码。其中包括了订阅DelegateSubscribe和发布DelegatePublish

namespace Event
{
    public class DelegatePublish
    {
        public int Value
        {
            get => _value;
            set
            {
                _value = value;
                Changed?.Invoke(_value);
            }
        }

        private int _value { get; set; }

        public Action<int> Changed;
    }

    public class DelegateSubscribe
    {
        public void ValueChanged(int value)
        {
            Console.WriteLine(value);
        }
    }
}

在主函数里面使用,一切顺利,它应该会被触发 Console.WriteLine(value);

// See https://aka.ms/new-console-template for more information

using Event;

var publish = new DelegatePublish();
var subscribe = new DelegateSubscribe();
publish.Changed += subscribe.ValueChanged;
publish.Value = 1;

一看没啥问题,订阅触发都正常,但这里有个可以操作的地方,就是这个changed可以被DelegatePublish以外的地方修改。如以下代码表示,实际上并没有值更新,但依旧可以触发。

// See https://aka.ms/new-console-template for more information

using Event;

var publish = new DelegatePublish();
var subscribe = new DelegateSubscribe();
publish.Changed = subscribe.ValueChanged;
publish.Changed.Invoke(1);

为了解决以上提到的问题,我们使用了event进行解决,只有直接持有这个事件的对象的类才可以调用事件,其他类只能使用+-和-=对这个事件进行添加或删除

public class DelegatePublish
{
    public int Value
    {
        get => _value;
        set
        {
            _value = value;
            OnChanged?.Invoke(this,new ValueArgs(_value));
        }
    }

    private int _value { get; set; }

    public event EventHandler<ValueArgs> OnChanged; 

    public class ValueArgs:EventArgs
    {
        public ValueArgs(int newValue)
        {
            NewValue = newValue;
        }

        public int NewValue { get; set; }
    }
}
void Publish_OnChanged(object? sender, DelegatePublish.ValueArgs e)

当然根据代码规范,其声明为object? sender表示委托对象的引用, EventArgs附带了数据

如果不喜欢参数e,还可以这么写,这样声明的就变成了 void Publish_OnChanged(object sender, DelegatePublish.ValueArgs newValue)

public event ChangedHandler OnChanged; 

public delegate void ChangedHandler(object sender, ValueArgs newValue);

事件的内部机制

DelegatePublish代码翻译成IL,可以发现 add_OnChanged remove_OnChanged分别表示 +=和-=。最后由addremove进行封装处理

.class public auto ansi beforefieldinit
  Event.DelegatePublish
    extends [System.Runtime]System.Object
{
  .field private class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> OnChanged
   
  .method public hidebysig specialname instance void
    add_OnChanged(
      class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> 'value'
    ) cil managed
  } // end of method DelegatePublish::add_OnChanged

  .method public hidebysig specialname instance void
    remove_OnChanged(
      class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> 'value'
    ) cil managed
  
  } // end of method DelegatePublish::remove_OnChanged
  .event class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs> OnChanged
  {
    .addon instance void Event.DelegatePublish::add_OnChanged(class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs>)
    .removeon instance void Event.DelegatePublish::remove_OnChanged(class [System.Runtime]System.EventHandler`1<class Event.DelegatePublish/ValueArgs>)
  } // end of event DelegatePublish::OnChanged
} // end of class Event.DelegatePublish

这样我们知道了事件内部是怎么处理的,就可以自定义+=和-=了

public class DelegatePublish
    {
        public int Value
        {
            get => _value;
            set
            {
                _value = value;
                _OnChanged?.Invoke(this, new ValueArgs(_value));
            }
        }

        public event EventHandler<ValueArgs> OnChanged
        {
            add => _OnChanged = (ChangedHandler)Delegate.Combine(value, _OnChanged);
            remove => _OnChanged = (ChangedHandler)Delegate.Remove(_OnChanged, value)!;
        }

        public delegate void ChangedHandler(object sender, ValueArgs newValue);

        private int _value { get; set; }

        protected event ChangedHandler? _OnChanged;

        public class ValueArgs : EventArgs
        {
            public ValueArgs(int newValue)
            {
                NewValue = newValue;
            }

            public int NewValue { get; set; }
        }
    }

 

posted @ 2022-12-31 01:30  樱花落舞  阅读(54)  评论(2编辑  收藏  举报