当前位置:  开发笔记 > 编程语言 > 正文

在C#中设置模块化程序的最佳方法

如何解决《在C#中设置模块化程序的最佳方法》经验,为你挑选了2个好方法。

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

机器人使用Regex从服务器中分离所有原始数据,然后使用数据触发相应的事件.例如,典型的事件处理程序可能如下所示:

OnChannelMessage(object sender, ChannelMessageEventArgs e)
{
}

In ChannelMessageEventArgs将是频道名称,发送者的昵称,消息等...

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

理想情况下,我希望能够动态编译.cs文件,并在plugin.cs文件中,将是一些事情:

    要捕获的事件,例如OnChannelMessage和OnChannelEventArgs中的Channeldata

    给出这些信息后该怎么办

    一个帮助文本(我可以从主机器人内部调用..所以说一个字符串,帮助="这是这个插件的帮助",可以随时返回,而无需实际调用插件)

    插件名称是什么等

感谢您对于在编程方面相对较新的人的起点.



1> dan..:

我之前在项目中使用过这样的东西,但已经有一段时间了.可能有框架为你做这种事情.

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

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

玩得开心!



2> bendewey..:

该托管扩展性框架(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 Modules { get; set; }


你可以自己编写框架代码......为什么要重新发明轮子?
推荐阅读
罗文彬2502852027
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有