Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to set up a modular program in C# [closed]

Tags:

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.

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:

OnChannelMessage(object sender, ChannelMessageEventArgs e) { } 

In ChannelMessageEventArgs would be the channel name, the nick of the sender, message etc...

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:

  1. what event to capture, for example, OnChannelMessage, and the Channeldata in OnChannelEventArgs
  2. what to do when this information is given,
  3. 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)
  4. what the plugin name is etc

Thanks for any ideas on where to start for someone who is relatively new at programming.

like image 991
caesay Avatar asked Dec 24 '09 20:12

caesay


People also ask

How can we achieve modular programming in C?

Modular programming consists of separating implementation from interface and hiding information in the implementation. In C this is achieved by placing the interface definition in a header file and the implementation in a source file. Disciplined use of static is used to hide implementation details.

How is modularity introduced in C?

Modularity is closely tied with encapsulation; think of modularity as a way of mapping encapsulated abstractions into real, physical modules. The C/C++ convention is to create two files for each class: a header file (. h suffix) for the class interface, and an implementation file (.

What are modular techniques?

The technique of breaking down one big solution into smaller modules for ease of development, implementation, modification and maintenance is called modular technique of programming or software development.


2 Answers

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.

//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.

 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!

like image 195
dan Avatar answered Sep 28 '22 10:09

dan


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.

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; } 
like image 42
bendewey Avatar answered Sep 28 '22 10:09

bendewey