c# - 在 C# 中设置模块化程序的最佳方式

标签 c# .net module

<分区>

我和我的 friend 正在编写一个 IRC C# 机器人,我们正在寻找一种包含模块系统的方法,以便用户可以编写自定义模块来扩展功能。

机器人使用 Regex 拆分来自服务器的所有原始数据,然后用数据触发适当的事件。例如,典型的事件处理程序可能如下所示:

OnChannelMessage(object sender, ChannelMessageEventArgs e)
{
}

ChannelMessageEventArgs 中将是 channel 名称、发件人的昵称、消息等...

我想要一个插件系统,以便人们可以在机器人加载或运行时随意构建模块并加载/卸载它们。

理想情况下,我希望能够即时编译 .cs 文件,并且在 plugin.cs 文件中,有几件事:

  1. 捕获什么事件,比如OnChannelMessage,OnChannelEventArgs中的Channeldata
  2. 收到此信息后该怎么办,
  3. 一个帮助文本(我可以从主机器人内部调用..所以说一个字符串,help =“这是这个插件的帮助”,可以在任何时候返回而不实际调用插件)<
  4. 插件名称等

对于编程方面相对较新的人,感谢您提供关于从哪里开始的任何想法。

最佳答案

我以前在项目中使用过类似的东西,但已经有一段时间了。可能有一些框架可以为您做这类事情。

要编写您自己的插件架构,基本上您需要为所有要实现的模块定义一个接口(interface),并将其放在您的程序和模块共享的程序集中:

public interface IModule
{
     //functions/properties/events of a module
} 

然后您的实现者会将他们的模块编码到这个程序集中,最好使用默认构造函数。

public class SomeModule : IModule {} ///stuff 

在你的程序中(假设你的模块将被编译成它们自己的程序集)你加载一个包含模块的程序集的引用,找到实现模块接口(interface)的类型,并实例化它们:

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));

查看有关插件架构和加载动态组件的教程,以更好地了解执行此类操作。这只是开始。一旦掌握了基础知识,您就可以开始做一些非常酷的事情了。

至于处理元数据(模块 X 名为 YYY,应该处理事件 A、B 和 C): 尝试将其应用到您的类型系统中。您可以为不同的功能/事件组成不同的接口(interface),或者您可以将所有功能放在一个接口(interface)上,并将属性(您将在共享程序集中声明这些)放在模块类上,使用属性来声明哪些事件该模块应该订阅。基本上,您想让人们为您的系统编写模块尽可能简单。

 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 {} 

玩得开心!

关于c# - 在 C# 中设置模块化程序的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1959727/

相关文章:

c# - 使用 nHibernate 序列化 DetachedCriteria

c# - NerdDinner DinnerFormViewModel 错误 CS0030 错误

c# - 需要有关 C# Twitch 机器人的帮助

c# - 使用 RGB 值设置 Excel 单元格颜色

perl - 如何使用 "use strict"导入常量,避免 "Can' t 使用 bareword ... 作为 ARRAY ref"

c# - 带后缀的字符串解析失败

c# - 如何跨不同的类返回多个变量

c# - 稳步减慢从 javascript 到 WCF 服务的 ajax 调用

javascript - 当命名导入不存在时,Webpack 静默失败

module - typescript 模块