我的朋友和我正在编写一个IRC C#bot,我们正在寻找一种方法来包含一个模块系统,以便用户可以编写自定义模块来扩展功能。
机器人使用Regex从服务器中分离所有原始数据,然后使用数据触发相应的事件。例如,典型的事件处理程序可能如下所示:
OnChannelMessage(object sender, ChannelMessageEventArgs e)
{
}
在ChannelMessageEventArgs
中将是频道名称,发送者的昵称,消息等......
我想拥有一个插件系统,以便人们可以随意构建模块并加载/卸载它们,当僵尸程序加载或运行时。
理想情况下,我希望能够动态编译.cs
文件,而在plugin.cs文件中,可能会有以下几点:
感谢任何有关在编程方面比较陌生的人从哪里开始的想法。
答案 0 :(得分:34)
我之前在项目中使用过这样的东西,但已经有一段时间了。可能有框架为你做这种事情。
要编写自己的插件架构,基本上您要为要实现的所有模块定义一个接口,并将其放在程序和模块共享的程序集中:
public interface IModule
{
//functions/properties/events of a module
}
然后,您的实现者将其模块编码到此程序集,最好使用默认构造函数。
public class SomeModule : IModule {} ///stuff
在你的程序中(假设你的模块将被编译成它们自己的程序集)你加载一个包含模块的程序集的引用,找到实现模块接口的类型,并实例化它们:
var moduleAssembly = System.Reflection.Assembly.LoadFrom("assembly file");
var moduleTypes = moduleAssembly.GetTypes().Where(t =>
t.GetInterfaces().Contains(typeof(IModule)));
var modules = moduleTypes.Select( type =>
{
return (IModule) Activator.CreateInstance(type);
});
如果你想动态编译代码:你创建一个编译器对象,告诉它要引用哪些程序集(系统和包含IModule的程序集,以及所需的任何其他引用),告诉它将源文件编译成集会。从那里,您获得导出的类型,过滤保留实现IModule的那些,并实例化它们。
//I made up an IModule in namespace IMod, with string property S
string dynamicCS = @"using System; namespace DYN
{ public class Mod : IMod.IModule { public string S
{ get { return \"Im a module\"; } } } }";
var compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler();
var options = new System.CodeDom.Compiler.CompilerParameters(
new string[]{"System.dll", "IMod.dll"});
var dynamicAssembly= compiler.CompileAssemblyFromSource(options, dynamicCS);
//you need to check it for errors here
var dynamicModuleTypes = dynamicAssembly.CompiledAssembly.GetExportedTypes()
.Where(t => t.GetInterfaces().Contains(typeof(IMod.IModule)));
var dynamicModules = dynModType.Select(t => (IMod.IModule)Activator.CreateInstance(t));
查看有关插件架构的教程并加载动态assmeblies,以便更好地了解这类内容。这只是开始。一旦你掌握了基础知识,你就可以开始做一些非常酷的事情了。
至于处理元数据(模块X名为YYY,应处理事件A,B和C): 尝试将其应用到您的类型系统中。您可以为不同的函数/事件组成不同的接口,或者您可以将所有函数放在一个接口上,并将属性(您在共享程序集中声明这些)放在模块类上,使用属性声明哪些事件该模块应订阅。基本上你想让人们为你的系统编写模块尽可能简单。
enum ModuleTypes { ChannelMessage, AnotherEvent, .... }
[Shared.Handles(ModuleTypes.ChannelMessage)]
[Shared.Handles(ModuleTypes.AnotherEvent)]
class SomeModule : IModule { ... }
或
//this is a finer-grained way of doing it
class ChannelMessageLogger : IChannelMessage {}
class PrivateMessageAutoReply : IPrivateMessage {}
玩得开心!
答案 1 :(得分:18)
Managed Extensibility Framework (MEF)提供了您正在寻找的内容,除非他们称之为插件架构。您可以提供用户可以构建的通用接口,然后MEF可以扫描目录中的dll并动态加载任何导出。
例如,您的插件作者可以创建类似
的内容[Export(typeof(IModule))]
public class MyModule : IModule
{
void HandleMessage(ChannelEventArgs e) {}
}
然后你的代码看起来像这样:
void OnChannelMessage(ChannelEventArgs e)
{
foreach(IModule module in Modules)
{
module.HandleMessage(e);
}
}
[Import]
public IEnumerable<IModule> Modules { get; set; }
答案 2 :(得分:3)
请查看微软的托管可扩展性框架(MEF) - 它建议您做一些您想要的事情,并提供许多其他功能:
答案 3 :(得分:3)
.NET 3.5的另一个选项是Managed AddIn Framework(MAF)。请查看以下链接。
示例和工具:http://clraddins.codeplex.com/
MAF博客:http://blogs.msdn.com/clraddins/
一篇好文章:http://blogs.microsoft.co.il/blogs/bursteg/archive/2007/08/13/Build-AddIns-with-System-AddIn.aspx
答案 4 :(得分:2)
MEF - Managed Extensibility Framework现在是流行语 - 我认为这是最合适的。
如果你没有做到这一点,你也可以查看Prism,他们有另一种方法来实现模块。