2009-12-24 23 views
17

Mi amigo y yo escribimos un bot de IRC C#, y estamos buscando una forma de incluir un sistema de módulos para que los usuarios puedan escribir módulos personalizados para extender la función.La mejor manera de configurar un programa modular en C#

El bot utiliza Regex para dividir todos los datos sin formato del servidor, y luego desencadena el evento apropiado con los datos. Por ejemplo, un controlador de eventos típica podría ser:

OnChannelMessage(object sender, ChannelMessageEventArgs e) 
{ 
} 

En ChannelMessageEventArgs sería el nombre del canal, el alias del remitente, mensajes, etc ...

Me gustaría tener un sistema de plugins para que las personas puedan construir módulos y cargarlos/descargarlos a voluntad, cuando se carga el bot o mientras se está ejecutando.

Idealmente me gustaría ser capaz de compilar los archivos .cs sobre la marcha, y en el archivo plugin.cs, habría algunas cosas:

  1. qué evento para capturar, por ejemplo, OnChannelMessage y los datos de canal en OnChannelEventArgs
  2. qué hacer cuando se da esta información,
  3. un texto de ayuda en (que puedo llamar desde adentro del bot principal ... así que diga una cadena, help = "esta es la ayuda para este complemento "que se puede devolver en cualquier momento sin llamar realmente al complemento)
  4. cuál es el nombre del complemento, etc.

Gracias por cualquier idea sobre por dónde empezar para alguien que es relativamente nuevo en la programación.

Respuesta

28

He usado este tipo de cosas en los proyectos antes, pero ha sido Un rato. Probablemente hay marcos por ahí que hacen este tipo de cosas para usted.

para escribir su propia arquitectura plug-in, básicamente desea definir una interfaz para todos los módulos para implementar y poner esto en un ensamblado compartido tanto por su programa y los módulos:

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

Entonces sus implementadores codificarán sus módulos en este ensamblaje, preferiblemente con un constructor predeterminado.

public class SomeModule : IModule {} ///stuff 

En su programa (suponiendo sus módulos se compila en sus propias asambleas) se carga una referencia a un conjunto que contiene un módulo, encontrará los tipos que implementan la interfaz del módulo, y crean instancias:

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

Si desea compilar el código sobre la marcha: crea un objeto compilador, le dice qué ensamblajes de referencia (el sistema y el que contiene IModule, más cualquier otra referencia necesaria), dígale que compile el archivo fuente en una montaje. A partir de eso, obtienes los tipos exportados, filtra los que mantienen aquellos que implementan el IModule y los instancias.

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

Mirar hacia arriba tutoriales en arquitecturas plug-in y la carga de assmeblies dinámicos para tener una mejor idea de hacer este tipo de cosas. Este es solo el comienzo. Una vez que hayas aprendido lo básico, puedes comenzar a hacer cosas realmente geniales.

En cuanto al manejo de los metadatos (el módulo X se llama YYY y debe manejar los eventos A, B y C): Trate de trabajar esto en su sistema de tipos. Podría crear diferentes interfaces para las diferentes funciones/eventos, o podría poner todas las funciones en una interfaz y poner atributos (los declararía en el ensamblado compartido) en las clases de módulos, usando los atributos para declarar qué eventos el módulo debe suscribirse a. Básicamente, debe hacer que sea lo más simple posible para las personas escribir módulos para su sistema.

enum ModuleTypes { ChannelMessage, AnotherEvent, .... } 

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

o

//this is a finer-grained way of doing it 
class ChannelMessageLogger : IChannelMessage {} 
class PrivateMessageAutoReply : IPrivateMessage {} 

divertirse!

+1

Recomiendo encarecidamente el uso de marcos de plugins existentes como MEF o System.AddIns; te ahorrarán muchos dolores de cabeza de plomería. –

+1

Yo también, pero el OP solicitó una solución que no sea de marco.Además, lo básico es bueno saber – dan

+1

Si pudiera volver a votar esto varias veces, lo haría. Aprendí mucho del código que me diste hace 3 años. – caesay

3

favor, eche un vistazo a Managed Extensibility Framework de Microsoft (MEF) - Se propone hacer algo de lo que quiere, y ofrece una gran cantidad de otras características:

Google results for 'managed extensibility framework'

+1

+1 también podría ser interesante observar que formará parte de .NET 4.0 –

16

El Managed Extensibility Framework (MEF) proporciona exactamente lo que están buscando, excepto que lo llaman una arquitectura de complemento. Podría proporcionar una interfaz común en la que los usuarios pudieran construir y luego MEF podría escanear un directorio de dlls y cargar cualquier Exportación dinámicamente.

Por ejemplo los autores de plugins podrían crear algo así como

[Export(typeof(IModule))] 
public class MyModule : IModule 
{ 
    void HandleMessage(ChannelEventArgs e) {} 
} 

A continuación, el código podría ser algo como esto:

void OnChannelMessage(ChannelEventArgs e) 
{ 
    foreach(IModule module in Modules) 
    { 
    module.HandleMessage(e); 
    } 
} 

[Import] 
public IEnumerable<IModule> Modules { get; set; } 
+0

¿Hay alguna manera de hacer algo como esto sin utilizar ningún tipo de Frameworks externos? – caesay

+1

Esto es de código abierto, por lo que puede ver, pero con el 'FileSystemWatcher',' Assembly.Load', y algunos reflejos, debería ser capaz de modelar algo similar con bastante facilidad. – bendewey

+3

Puede escribir el código de marco usted mismo ... ¿Por qué reinventar la rueda? – Paolo

1

MEF - Managed Extensibility Framework es la palabra de moda en este momento - yo creo que sería el mejor ajuste.

Si eso no funciona, también puede buscar en Prism, tienen otro enfoque para implementar módulos.

Cuestiones relacionadas