Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用C语言建立模块化程序的最佳方法#_C#_.net_Module - Fatal编程技术网

C# 用C语言建立模块化程序的最佳方法#

C# 用C语言建立模块化程序的最佳方法#,c#,.net,module,C#,.net,Module,我的朋友和我正在编写一个IRC C#bot,我们正在寻找一种包含模块系统的方法,以便用户可以编写自定义模块来扩展功能 bot使用Regex分割服务器上的所有原始数据,然后用这些数据触发相应的事件。例如,典型的事件处理程序可能如下所示: OnChannelMessage(object sender, ChannelMessageEventArgs e) { } 在ChannelMessageEventArgs中,频道名称、发送者的昵称、消息等 我希望有一个插件系统,这样人们可以构建模块,并在机器

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

bot使用Regex分割服务器上的所有原始数据,然后用这些数据触发相应的事件。例如,典型的事件处理程序可能如下所示:

OnChannelMessage(object sender, ChannelMessageEventArgs e)
{
}
ChannelMessageEventArgs
中,频道名称、发送者的昵称、消息等

我希望有一个插件系统,这样人们可以构建模块,并在机器人加载或运行时随意加载/卸载它们

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

  • 要捕获的事件,例如OnChannelMessage和OnChannelEventArgs中的Channeldata
  • 当提供此信息时,该怎么办
  • 中的帮助文本(我可以从主bot内调用它..所以说一个字符串,help=“这是此插件的帮助”,可以在任何时候返回,而无需实际调用插件)
  • 插件名是什么等等

  • 感谢对编程相对较新的人提出从何处开始的建议。

    请看一看microsoft的托管可扩展性框架(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; }
    
    void OnChannelMessage(ChannelEventArgs e)
    {
    foreach(模块中的IModule模块)
    {
    手机短信模块(e);
    }
    }
    [进口]
    公共IEnumerable模块{get;set;}
    
    我以前在项目中使用过类似的东西,但已经有一段时间了。可能有一些框架可以为您完成这类工作

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

    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));
    
    查阅有关插件体系结构和加载动态组件的教程,以更好地了解如何完成这类工作。这只是一个开始。一旦你掌握了基本知识,你就可以开始做一些非常酷的事情了

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

     enum ModuleTypes { ChannelMessage, AnotherEvent, .... }
    
     [Shared.Handles(ModuleTypes.ChannelMessage)]
     [Shared.Handles(ModuleTypes.AnotherEvent)]
     class SomeModule : IModule { ... }
    


    玩得开心

    在.NET3.5的底层还有另一个选项,称为托管加载项框架(MAF)。查看以下链接

    示例和工具:

    MAF博客:


    一篇好文章:

    MEF-托管可扩展性框架是目前的流行语-我认为这将是最合适的


    如果这不符合您也可以查阅的技巧,那么他们有另一种实现模块的方法。

    +1您可能还需要注意的是,它将成为.NET 4.0的一部分。有没有一种方法可以在不使用任何类型的外部框架的情况下完成类似的操作?这是开源的,因此您可以查看,但是有了
    FileSystemWatcher
    Assembly.Load
    ,以及一些反射,您应该能够相当容易地对类似的东西进行建模。您可以自己编写框架代码。。。为什么要重新发明轮子呢?进口方面要比出口方面困难得多。您可能希望避免使用setter注入块,而只使用helper函数来获得导入。@Paolo同意,特别是因为这是随附的。NET4I强烈建议使用现有的插件框架,如MEF或System.AddIns;他们会帮你省去很多管道方面的麻烦。我也是,但OP要求一个非框架解决方案。再加上最基本的是很高兴知道如果我能再投票几次,我会的。我从你3年前提供的代码中学到了很多,“如果你想动态编译代码”。我为什么要那样?我想不出理由。我是说,我应该事先准备好插件DLL。为什么我要在程序中创建这些DLL,而不是在visual studio、msbuild或生成服务器中创建它们?@sotn你是对的,你几乎从来都不想这样!这几乎总是个糟糕的主意。我见过一些有效使用动态编译的东西:asp.NETMVC中的cshtml文件;林帕德;独立代码查询。我知道的另一个真实的用例是,在哪里可以使用这个(但不应该):想象一下,您想让用户在生产网站上生成任意报告。有很多方法可以做到这一点,但一种(危险的)方法是让用户以html形式编写linq查询,然后让服务器编译并针对数据源执行查询
     //this is a finer-grained way of doing it
     class ChannelMessageLogger : IChannelMessage {}
     class PrivateMessageAutoReply : IPrivateMessage {}