2009-12-24 9 views
17

Bạn tôi và tôi đang viết một bot IRC C#, và chúng tôi đang tìm cách bao gồm hệ thống mô-đun để người dùng có thể viết mô-đun tùy chỉnh để mở rộng chức năng.Cách tốt nhất để thiết lập chương trình mô-đun ở C#

bot sử dụng Regex để phân tách tất cả dữ liệu thô từ máy chủ và sau đó kích hoạt sự kiện thích hợp với dữ liệu. Ví dụ, một xử lý sự kiện điển hình có thể trông giống như:

OnChannelMessage(object sender, ChannelMessageEventArgs e) 
{ 
} 

Trong ChannelMessageEventArgs sẽ là tên kênh, nick của người gửi, tin nhắn vv ...

Tôi muốn có một hệ thống plugin để mọi người có thể xây dựng mô-đun và tải/giải phóng chúng theo ý muốn, khi bot tải hoặc trong khi chạy.

Lý tưởng nhất là tôi muốn để có thể biên dịch .cs file một cách nhanh chóng, và trong file plugin.cs, sẽ có một vài điều:

  1. gì sự kiện để nắm bắt, ví dụ, OnChannelMessage và Channeldata trong OnChannelEventArgs
  2. phải làm gì khi thông tin này được cung cấp,
  3. văn bản trợ giúp (mà tôi có thể gọi từ bên trong bot chính .. vì vậy hãy nói một chuỗi, help = "đây là trợ giúp cho plugin này "có thể được trả lại bất kỳ lúc nào mà không thực sự gọi plugin)
  4. tên plugin là gì?

Cảm ơn mọi ý tưởng về nơi bắt đầu cho người mới bắt đầu lập trình.

Trả lời

28

Tôi đã sử dụng những thứ như thế này trên các dự án trước đó, nhưng nó được một lúc. Có thể có các khuôn khổ ngoài kia làm việc này cho bạn.

Để viết riêng plug-in kiến ​​trúc của bạn, về cơ bản bạn muốn xác định một giao diện cho tất cả các mô-đun để thực hiện, và đặt điều này trong một assembly chia sẻ của cả hai chương trình của bạn và các module:

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

Sau đó, những người triển khai của bạn sẽ mã hóa các mô đun của họ cho assembly này, tốt hơn là với một constructor mặc định.

public class SomeModule : IModule {} ///stuff 

Trong chương trình của bạn (giả module của bạn sẽ được biên dịch vào lắp ráp riêng của họ), bạn tải một tài liệu tham khảo một hội đồng có chứa một mô-đun, tìm ra loại mà thực hiện các giao diện mô-đun, và nhanh chóng chúng:

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

Nếu bạn muốn biên dịch mã khi đang chạy: bạn tạo một đối tượng trình biên dịch, hãy cho nó biết những gì cần tham khảo (Hệ thống và phần chứa IModule, cộng với bất kỳ tham chiếu nào khác cần thiết), cho nó biên dịch tập tin nguồn thành hội,, tổ hợp. Từ đó, bạn nhận được các loại được xuất, bộ lọc giữ những loại thực hiện IModule và khởi tạo chúng.

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

Tìm hướng dẫn về kiến ​​trúc trình cắm và tải assmeblies động để có cảm giác tốt hơn khi thực hiện loại công cụ này. Đây mới chỉ là khởi đầu. Một khi bạn nhận được những điều cơ bản xuống, bạn có thể bắt đầu làm một số điều thực sự mát mẻ.

Để xử lý siêu dữ liệu (mô-đun X có tên YYY và nên xử lý các sự kiện A, B và C): Hãy thử làm việc này với hệ thống kiểu của bạn. Bạn có thể tạo ra các giao diện khác nhau cho các chức năng/sự kiện khác nhau, hoặc bạn có thể đặt tất cả các chức năng trên một giao diện và đặt các thuộc tính (bạn khai báo chúng trong assembly chung) trên các lớp module, sử dụng các thuộc tính để khai báo sự kiện nào mô-đun nên đăng ký. Về cơ bản bạn muốn làm cho nó càng đơn giản càng tốt để mọi người viết các mô-đun cho hệ thống của bạn.

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

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

hoặc

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

vui chơi!

+1

Tôi rất muốn khuyên bạn nên sử dụng các khuôn khổ plugin hiện có như MEF hoặc System.AddIns; họ sẽ giúp bạn tiết kiệm rất nhiều đau đầu ống dẫn nước. –

+1

Tôi cũng vậy, nhưng OP đã yêu cầu giải pháp phi khung.Cộng với những điều cơ bản là tốt đẹp để biết – dan

+1

Nếu tôi có thể upvote này một lần nữa một vài lần, tôi sẽ. Tôi đã học được rất nhiều từ mã bạn cung cấp cách đây 3 năm. – caesay

3

hãy có một cái nhìn một Khả năng mở rộng khung quản lý của microsoft (MEF) - Báo cáo đề xuất để làm một số những gì bạn muốn, và cung cấp rất nhiều tính năng khác:

Google results for 'managed extensibility framework'

+1

+1 nó cũng có thể là thú vị để lưu ý rằng nó sẽ là một phần của .NET 4.0 –

16

Các Managed Extensibility Framework (MEF) cung cấp chính xác những gì bạn đang tìm kiếm ngoại trừ họ gọi nó là một kiến ​​trúc Plugin. Bạn có thể cung cấp một giao diện chung mà người dùng của bạn có thể xây dựng và sau đó MEF có thể quét một thư mục cho các dll và tải bất kỳ Xuất động động nào.

Ví dụ tác giả plugin của bạn có thể tạo ra một cái gì đó giống như

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

Sau đó, mã của bạn có thể trông giống như thế này:

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

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

Có cách nào để làm một cái gì đó như thế này mà không sử dụng bất kỳ loại khung bên ngoài? – caesay

+1

Đây là mã nguồn mở, vì vậy bạn có thể xem, nhưng với 'FileSystemWatcher',' Assembly.Load', và một số phản ánh bạn sẽ có thể mô hình một cái gì đó tương tự khá dễ dàng. – bendewey

+3

Bạn có thể viết mã khung chính mình ... Tại sao lại phát minh ra bánh xe? – Paolo

1

MEF - Managed Khả năng mở rộng Framework là từ thông dụng ngay bây giờ - Tôi nghĩ đó sẽ là phù hợp nhất.

Nếu điều đó không thực hiện thủ thuật, bạn cũng có thể tra cứu trên Prism, họ có cách tiếp cận khác để triển khai mô-đun.