Action

在 C# 中,Action 是一种无返回值的泛型委托类型。

想象一个场景:

假设小明遇到了一位他非常喜欢的视频博主,不想错过该博主发布的每一个视频。于是小明订阅了该博主,而博主每次发布新内容的消息都会第一时间传递到小明手上。这样,小明就知道博主更新了视频,并立刻开始收看。

class Xiaoming
{
    VedioPublisher _Vb;
    public Xiaoming(VedioPublisher Vb)
    {
        _Vb = Vb;
        _Vb.OnVideoPublished += WatchVideo;
    }
    private void WatchVideo()
    {
        Console.WriteLine("小明收到通知,立刻启动电视!");
    }
};
class VedioPublisher
{
   public Action OnVideoPublished;
   public void PublishVideo(string title)
   {
     Console.WriteLine($"博主发布了新视频:{title}");
     OnVideoPublished?.Invoke();
   }
};

整个流程:

小明订阅博主 → 博主发布视频 → 小明得知视频更新 → 小明收看视频。

但是小明等不及视频博主的更新,于是自己手动调用通知:

_Vb.OnVideoPublished?.Invoke();

这违背了订阅的初衷,应该是博主通知小明,而不是小明自己触发通知。

对于订阅者/发布者模式,我们希望订阅者只能执行订阅和退订的操作

为了解决这个问题,博主把 Action 封装进 event

class VedioPublisher
{
   public event Action OnVideoPublished;
   public void PublishVideo(string title)
   {
     Console.WriteLine($"博主发布了新视频:{title}");
     OnVideoPublished?.Invoke();
   }
};

此时小明就不能再自己触发事件了,他只能等待视频博主发布更新的消息。

可是问题又来了:小明想知道视频发布的具体时间,但原本的订阅通道并没有提供这个信息。

那么该怎么解决呢?

class Xiaoming
{
    VedioPublisher _Vb;
    
    public Xiaoming(VedioPublisher Vb)
    {
        _Vb = Vb;
        _Vb.OnVideoPublished += WatchVideo;
        _Vb.OnVideoPublishedWithTime += WatchVideoWithTime;
    }
    private void WatchVideo()
    {
        Console.WriteLine("小明收到通知,立刻启动电视!");
    }
    private void WatchVideoWithTime(string time)
    {
        Console.WriteLine($"视频发布时间为:{time}");
    }
};
class VedioPublisher
{
   public event Action OnVideoPublished;
   public event Action OnVideoPublishedWithTime;
   public void PublishVideo(string title)
   {
     Console.WriteLine($"博主发布了新视频:{title}");
     OnVideoPublished?.Invoke();
   }
   public void PublishVideoWithTime(string title, string time)
   {
     Console.WriteLine($"博主发布了新视频:{title}");
     OnVideoPublishedWithTime?.Invoke(time);
   }
};

小明查阅资料发现,视频博主同时提供了另一个订阅渠道 OnVideoPublishedWithTime,于是他又订阅了该事件。但这个订阅渠道有个特殊规定:每次通知时必须传入一个参数,否则不会触发通知。

于是小明编写了符合规则的方法 WatchVideoWithTime,完美匹配了 Action<string> 的要求,从此可以正常接收通知。

其实这就是 Action<T> 的实际运用和规则:订阅的方法的参数必须与 Action 模板一致,否则会报错。

最后补充一个点:在同一个Action委托内,函数触发顺序和订阅顺序是一致的

telegate

自定义委托类型,可以自定义函数签名,函数返回值

public telegate void Pb();
//等价于Action,模板都为无参无返回类型委托