Tôi đang làm chính xác những gì bạn đang tìm kiếm trong công cụ quy tắc của tôi, công cụ này sử dụng CS-Script để biên dịch động, tải và chạy C #. Nó sẽ dễ dàng được dịch sang những gì bạn đang tìm kiếm và tôi sẽ đưa ra một ví dụ. Đầu tiên, mã (rút gọn):
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CSScriptLibrary;
namespace RulesEngine
{
public class RulesEngine<T> where T : class
{
public RulesEngine(string rulesScriptFileName, string classToInstantiate)
: this()
{
if (rulesScriptFileName == null) throw new ArgumentNullException("rulesScriptFileName");
if (classToInstantiate == null) throw new ArgumentNullException("classToInstantiate");
if (!File.Exists(rulesScriptFileName))
{
throw new FileNotFoundException("Unable to find rules script", rulesScriptFileName);
}
RulesScriptFileName = rulesScriptFileName;
ClassToInstantiate = classToInstantiate;
LoadRules();
}
public T @Interface;
public string RulesScriptFileName { get; private set; }
public string ClassToInstantiate { get; private set; }
public DateTime RulesLastModified { get; private set; }
private RulesEngine()
{
@Interface = null;
}
private void LoadRules()
{
if (!File.Exists(RulesScriptFileName))
{
throw new FileNotFoundException("Unable to find rules script", RulesScriptFileName);
}
FileInfo file = new FileInfo(RulesScriptFileName);
DateTime lastModified = file.LastWriteTime;
if (lastModified == RulesLastModified)
{
return;
}
string rulesScript = File.ReadAllText(RulesScriptFileName);
Assembly compiledAssembly = CSScript.LoadCode(rulesScript, null, true);
@Interface = compiledAssembly.CreateInstance(ClassToInstantiate).AlignToInterface<T>();
RulesLastModified = lastModified;
}
}
}
Thao tác này sẽ đưa một giao diện kiểu T, biên dịch tệp .cs thành một hợp ngữ, khởi tạo một lớp thuộc một kiểu nhất định và căn chỉnh lớp đã khởi tạo đó với giao diện T. Về cơ bản, bạn chỉ cần đảm bảo rằng lớp khởi tạo triển khai giao diện đó. Tôi sử dụng thuộc tính để thiết lập và truy cập mọi thứ, như vậy:
private RulesEngine<IRulesEngine> rulesEngine;
public RulesEngine<IRulesEngine> RulesEngine
{
get
{
if (null == rulesEngine)
{
string rulesPath = Path.Combine(Application.StartupPath, "Rules.cs");
rulesEngine = new RulesEngine<IRulesEngine>(rulesPath, typeof(Rules).FullName);
}
return rulesEngine;
}
}
public IRulesEngine RulesEngineInterface
{
get { return RulesEngine.Interface; }
}
Ví dụ của bạn, bạn muốn gọi Run (), vì vậy tôi sẽ tạo một giao diện xác định phương thức Run (), như sau:
public interface ITestRunner
{
void Run();
}
Sau đó, tạo một lớp triển khai nó, như thế này:
public class TestRunner : ITestRunner
{
public void Run()
{
}
}
Thay đổi tên của RulesEngine thành một cái gì đó giống như TestHarness và đặt thuộc tính của bạn:
private TestHarness<ITestRunner> testHarness;
public TestHarness<ITestRunner> TestHarness
{
get
{
if (null == testHarness)
{
string sourcePath = Path.Combine(Application.StartupPath, "TestRunner.cs");
testHarness = new TestHarness<ITestRunner>(sourcePath , typeof(TestRunner).FullName);
}
return testHarness;
}
}
public ITestRunner TestHarnessInterface
{
get { return TestHarness.Interface; }
}
Sau đó, bất cứ nơi nào bạn muốn gọi nó, bạn chỉ cần chạy:
ITestRunner testRunner = TestHarnessInterface;
if (null != testRunner)
{
testRunner.Run();
}
Nó có thể sẽ hoạt động tốt cho một hệ thống plugin, nhưng mã của tôi bị giới hạn ở việc tải và chạy một tệp, vì tất cả các quy tắc của chúng tôi đều nằm trong một tệp nguồn C #. Tuy nhiên, tôi nghĩ rằng sẽ khá dễ dàng để sửa đổi nó để chỉ chuyển vào tệp loại / nguồn cho mỗi tệp bạn muốn chạy. Bạn chỉ cần chuyển mã từ getter vào một phương thức lấy hai tham số đó.
Ngoài ra, hãy sử dụng IRunnable của bạn thay cho ITestRunner.
export
vàimport
các lớp học trong hội đồng riêng biệt bắt nguồn từ một giao diện nổi tiếng