在 C# 中设置模块化程序的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1959727/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to set up a modular program in C#
提问by caesay
My friend and I are writing an IRC C# bot, and we are looking for a way to include a module system so that users can write custom modules to extend function.
我和我的朋友正在编写一个 IRC C# bot,我们正在寻找一种方法来包含一个模块系统,以便用户可以编写自定义模块来扩展功能。
The bot uses Regex to split up all raw data from the server, and then triggers the appropriate event with the data. For example, a typical event handler might look like:
bot 使用 Regex 拆分来自服务器的所有原始数据,然后使用数据触发相应的事件。例如,典型的事件处理程序可能如下所示:
OnChannelMessage(object sender, ChannelMessageEventArgs e)
{
}
In ChannelMessageEventArgs
would be the channel name, the nick of the sender, message etc...
在ChannelMessageEventArgs
将通道名,发件人的千钧一发之际,消息等等...
I'd like to have a plugin system so that people can build modules and load / unload them at will, when the bot loads, or while its running.
我想要一个插件系统,以便人们可以在机器人加载时或运行时随意构建模块并加载/卸载它们。
Ideally I'd like to be able to compile the .cs
files on the fly, and in the plugin.cs file, would be a few things:
理想情况下,我希望能够.cs
即时编译文件,在 plugin.cs 文件中,将有以下几点:
- what event to capture, for example, OnChannelMessage, and the Channeldata in OnChannelEventArgs
- what to do when this information is given,
- a help text in (that i can call from inside the main bot.. so say a string, help = "this is the help for this plugin" that can be returned at any time without actually calling the plugin)
- what the plugin name is etc
- 要捕获什么事件,例如 OnChannelMessage 和 OnChannelEventArgs 中的 Channeldata
- 提供此信息后该怎么办,
- 一个帮助文本(我可以从主机器人内部调用......所以说一个字符串,help =“这是这个插件的帮助”,可以在不实际调用插件的情况下随时返回)
- 插件名称是什么等
Thanks for any ideas on where to start for someone who is relatively new at programming.
感谢您对编程方面相对较新的人从哪里开始的任何想法。
采纳答案by dan
I've used stuff like this on projects before, but it's been a while. There are probably frameworks out there that do this kind of stuff for you.
我以前在项目中使用过这样的东西,但已经有一段时间了。可能有一些框架可以为你做这种事情。
To write your own plug-in architecture, basically you want to define an interface for all the modules to implement, and put this in an assembly shared by both your program and the modules:
要编写自己的插件架构,基本上您要为所有要实现的模块定义一个接口,并将其放入程序和模块共享的程序集中:
public interface IModule
{
//functions/properties/events of a module
}
Then your implementors will code their modules to this assembly, preferably with a default constructor.
然后你的实现者将他们的模块编码到这个程序集中,最好使用默认构造函数。
public class SomeModule : IModule {} ///stuff
In your program (supposing your modules will be compiled into their own assemblies) you load a reference an assembly containing a module, find the types that implement the module interface, and instantiate them:
在你的程序中(假设你的模块将被编译成它们自己的程序集)你加载一个包含模块的程序集的引用,找到实现模块接口的类型,并实例化它们:
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);
});
If you want to compile the code on the fly: you create a compiler object, tell it what assemblies to reference (System and the one that contains IModule, plus any other references needed), tell it to compile the source file into an assembly. From that, you get the exported types, filter keeping those which implement the IModule, and instantiate them.
如果您想即时编译代码:您创建一个编译器对象,告诉它要引用哪些程序集(System 和包含 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));
Look up tutorials on plug-in architectures and loading dynamic assmeblies to get a better feel for doing this kind of stuff. This is just the start. Once you get the basics down, you can start doing some really cool things.
查找有关插件架构和加载动态程序集的教程,以更好地了解执行此类操作。这只是开始。一旦你掌握了基础知识,你就可以开始做一些非常酷的事情。
As for handling the metadata (module X is named YYY and should handle events A,B and C): Try to work this into your type system. You could make up different interfaces for the different functions/events, or you could put all the functions on one interface, and put attributes (you'd declare these in the shared assembly) on the module classes, using the attributes to declare which events the module should subscribe to. Basically you want to make it as simple as possible for people to write modules for your system.
至于处理元数据(模块 X 被命名为 YYY 并且应该处理事件 A、B 和 C):尝试将其应用到您的类型系统中。您可以为不同的函数/事件组成不同的接口,或者您可以将所有函数放在一个接口上,并将属性(您将在共享程序集中声明这些)放在模块类上,使用属性来声明哪些事件模块应该订阅。基本上,您想让人们为您的系统编写模块尽可能简单。
enum ModuleTypes { ChannelMessage, AnotherEvent, .... }
[Shared.Handles(ModuleTypes.ChannelMessage)]
[Shared.Handles(ModuleTypes.AnotherEvent)]
class SomeModule : IModule { ... }
or
或者
//this is a finer-grained way of doing it
class ChannelMessageLogger : IChannelMessage {}
class PrivateMessageAutoReply : IPrivateMessage {}
have fun!
玩得开心!
回答by Aviad P.
Please have a look a microsoft's Managed Extensibility Framework (MEF) - It proposes to do some of what you want, and provides a lot of other features:
请看看微软的托管扩展框架 (MEF) - 它建议做一些你想做的事情,并提供许多其他功能:
回答by bendewey
The Managed Extensibility Framework (MEF)provides exactly what you are looking for except they call it a Plugin architecture. You could provide a common interface which you users could build on and then MEF could scan a directory for dlls and load any Exports dynamically.
该托管扩展性框架(MEF)提供您正在寻找什么,除了他们称之为一个插件架构。您可以提供一个通用接口,您的用户可以在其上构建,然后 MEF 可以扫描目录中的 dll 并动态加载任何导出。
For example your plugin authors could create something like
例如,您的插件作者可以创建类似
[Export(typeof(IModule))]
public class MyModule : IModule
{
void HandleMessage(ChannelEventArgs e) {}
}
Then your code could look something like this:
那么您的代码可能如下所示:
void OnChannelMessage(ChannelEventArgs e)
{
foreach(IModule module in Modules)
{
module.HandleMessage(e);
}
}
[Import]
public IEnumerable<IModule> Modules { get; set; }
回答by Mehdi Golchin
There is another option in the hearth of .NET 3.5 called Managed AddIn Framework (MAF). Check the following links out.
.NET 3.5 的核心还有另一个选项,称为托管加载项框架 (MAF)。检查以下链接。
Sample and Tools: http://clraddins.codeplex.com/
示例和工具:http: //clraddins.codeplex.com/
MAF Blog: http://blogs.msdn.com/clraddins/
MAF 博客:http: //blogs.msdn.com/clraddins/
A good article: http://blogs.microsoft.co.il/blogs/bursteg/archive/2007/08/13/Build-AddIns-with-System-AddIn.aspx
一篇好文章:http: //blogs.microsoft.co.il/blogs/bursteg/archive/2007/08/13/Build-AddIns-with-System-AddIn.aspx