插件系统
定义
定义
一、说明
插件系统是一组能实现多播订阅的,可中断的触发器,其主要功能就是实现类似事件、委托的通知消息。其设计核心来自于AspNetCore
的中间件,它有着和中间件一样的使用体验,同时也有着更高的灵活性和自由度。
二、产品特点
- 简单易用。
- 易扩展。
三、产品应用场景
- 所有可以使用事件。委托的场景。
四、与事件、委托相比
- 订阅的时候可以不用知道被订阅方是谁,只需要知道要订阅什么通知即可。
- 订阅可以随时中断。例如:当事件多播的时候,即使其中一个订阅方已经处理,触发也不会停止,这样会造成资源浪费。
- 订阅回调。例如:第一个订阅方,想知道本次触发的最终结果是否已被处 理时,委托则做不到。而插件则可以。
- 插件可以被继承,可以被扩展。
- 插件可以被注入。
- 插件可以独立负责相关功能,实现功能独立。可模块化功能。
五、创建插件
5.1 新建插件接口及事件类
public class MyPluginEventArgs : PluginEventArgs
{
public string Words { get; set; }
}
/// <summary>
/// 定义一个插件接口,使其继承<see cref="IPlugin"/>
/// </summary>
public interface ISayPlugin : IPlugin
{
/// <summary>
/// Say。定义一个插件方法,必须遵循:
/// 1.必须是两个参数,第一个参数可以是任意类型,一般表示触发源。第二个参数必须继承<see cref="PluginEventArgs"/>
/// 2.返回值必须是Task。
/// </summary>
/// <param name="sender">触发主体</param>
/// <param name="e">传递参数</param>
/// <returns></returns>
Task Say(object sender, MyPluginEventArgs e);
}
注意事项
确定插件唯一的是插件中的类型
,所以要求一个插件中只允许有一个插件方法。
5.2 实现插件接口
新建一个类,实现ISayPlugin
接口即可。不过为了方便,也可先继承PluginBase
基类,然后实现所需的插件接口。
public class SayHelloPlugin : PluginBase, ISayPlugin
{
public async Task Say(object sender, MyPluginEventArgs e)
{
Console.WriteLine($"{this.GetType().Name}------Enter");
if (e.Words == "hello")
{
Console.WriteLine($"{this.GetType().Name}------Say");
//当满足的时候输出,且不在调用下一个插件。
//亦或者设置e.Handled = true,即使调用下一个插件,也会无效
return;
}
await e.InvokeNext();
Console.WriteLine($"{this.GetType().Name}------Leave");
}
}
public class SayHiPlugin : PluginBase, ISayPlugin
{
public async Task Say(object sender, MyPluginEventArgs e)
{
Console.WriteLine($"{this.GetType().Name}------Enter");
if (e.Words == "hi")
{
Console.WriteLine($"{this.GetType().Name}------Say");
//当满足的时候输出,且不在调用下一个插件。
//亦或者设置e.Handled = true,即使调用下一个插件,也会无效
return;
}
await e.InvokeNext();
Console.WriteLine($"{this.GetType().Name}------Leave");
}
}
internal class LastSayPlugin : PluginBase, ISayPlugin
{
public async Task Say(object sender, MyPluginEventArgs e)
{
Console.WriteLine($"{this.GetType().Name}------Enter");
Console.WriteLine($"您输入的{e.Words}似乎不被任何插件处理");
await e.InvokeNext();
Console.WriteLine($"{this.GetType().Name}------Leave");
}
}
提示
实现的插件,建议继承PluginBase
,然后实现所需的插件接口,这样能简化实现过程。但是如果该类型已经拥有基类,则直接实现所需插件接口的全部内容即可。
六、订阅插件
在插件触发前,需要先订阅插件。这样才知道哪些插件可以处理该数据。
6.1 创建插件管理器
IPluginManager pluginManager = new PluginManager(new Container())
{
Enable = true//必须启用
};
6.2 添加订阅插件
6.2.1 按类型添加
//添加订阅插件
pluginManager.Add<SayHelloPlugin>();
pluginManager.Add<SayHiPlugin>();
pluginManager.Add<LastSayPlugin>();
6.2.2 按实例添加
pluginManager.Add(new SayHelloPlugin());
pluginManager.Add(new SayHiPlugin());
pluginManager.Add(new LastSayPlugin());
6.2.3 按委托添加
委托添加插件有多个重载,下面一一为例:
pluginManager.Add(typeof(ISayPlugin), () =>
{
//无参委托,一般做通知
Console.WriteLine("在Action1中获得");
});
pluginManager.Add(typeof(ISayPlugin), async (MyPluginEventArgs e) =>
{
//只1个指定参数,当参数是事件参数时,需要主动InvokeNext
Console.WriteLine("在Action2中获得");
await e.InvokeNext();
});
pluginManager.Add(typeof(ISayPlugin), async (client, e) =>
{
//2个不指定参数,需要主动InvokeNext
Console.WriteLine("在Action3中获得");
await e.InvokeNext();
});
pluginManager.Add(typeof(ISayPlugin), async (object client, MyPluginEventArgs e) =>
{
//2个指定参数,需要主动InvokeNext
Console.WriteLine("在Action3中获得");
await e.InvokeNext();
});
提示
需不需要InvokeNext
,只需要记住一点,委托中是否接收了PluginEventArgs
派生的事件参数,如果是,则需要主动调用。