Sun_Blue_Sky

菩提本无树,明镜亦非台,本来无一物,何处惹尘埃 寻求内心的平静
随笔 - 21, 文章 - 92, 评论 - 75, 阅读 - 10万
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

插件?脚本?

Posted on   Sun_Blue_Sky  阅读(396)  评论(0编辑  收藏  举报
很多时候,我们需要通过插件(add-in)方式来载入不同的策略。举个例子,在垂直搜索引擎开发中,爬虫抓取回来的数据必须进行分析获取元数据后才能用于搜索。我们很难用通用文本特征抽取算法来获取不同网站的精确数据,此时我们必须为某些站点编写特定的 "算法"。由于目标种子站点在运营过程中随时都可能发生变化,从设计角度来说,我们自然不能把这些特殊策略全部放到核心类库里。可行的做法是 —— "外挂",将每个特定的策略做成独立插件,通过特定的手段动态载入并执行,以此来达到在不影响整体系统的情况下,进行扩充和变更的目的。

目前在 .NET 环境下,可选的 "插件模型" 包括:

1. 程序集方式
  • System.Addin : .NET 3.5 BCL 提供的一个插件扩展模型,功能十分强大,不过整体有些偏 "重",过于复杂。
  • System.Reflection : 使用反射过于繁琐了些。
  • AOP : 基本和反射类似,不过可以通过配置文件来简化操作。
程序集模式需要我们提供编译后的程序集,这对于频繁变化的系统来说不是个好选择。

2. 脚本方式
  • System.CodeDom : 这个不错,不过要处理的细节也不少,诸如编译过程,引用及参数设置等等。
  • IronPython、Ruby.NET : 很多网络游戏服务器,都采取动态脚本来编写 "剧情",服务器本身只是一个执行环境。只是选择 Python、Ruby 需要不同的语言整合,有点麻烦。
  • CS-Script : 这个东西基本上是对 CodeDom 的封装,它为我们完成了所有的细节处理。

经过对比,CS-Script 更符合我的需求。这样一来,我们只需要将策略写成文本代码文件,放到特定区域,然后由 "Shell" 载入执行即可。就算修改,也只需一个记事本就能完成,更何况使用的还是我们熟悉的 C# 语言。(记事本 + 命令行编译器 + 命令行调试器 ~~~~ 不要学我们这的某个同事,要在服务器上装一整套 VS2005。 ~~~~)

看一个例子。
Server.exe

 

复制代码
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CSScriptLibrary;

namespace Server
{
    
// 服务器上下文环境
    public class MyContent
    {
        
public int I { getset; }
    }

    
// 插件接口
    public interface IAddin 
    {
        
void Execute(MyContent content);
    }

    
class Program
    {
        
static void Main(string[] args)
        {
            
// 载入策略脚本
            var assembly = CSScript.Load("TestAddin.cs");
            
            
// 查找实现了插件接口的类型
            var q = (from type in assembly.GetTypes() 
                     
where type.GetInterface("Server.IAddin"true!= null select type).SingleOrDefault();

            
// 创建环境上下文
            var content = new MyContent { I = 12345 };
            
            
// 创建插件对象 (当然,我们可以缓存该对象,没必要每次执行都创建一个实例)
            var addin = new AsmHelper(assembly).CreateObject(q.Name) as IAddin;
            
            
// 调用插件方法
            addin.Execute(content);
        }
    }
}
复制代码

 

TestAddin.cs

复制代码
//css_ref Server.exe;
using System;
using Learn.CUI;

public class TestAddin : IAddin
{
    
public void Execute(MyContent content)
    {
        Console.WriteLine(content.I);
    }
}
复制代码

 

当然,我们还可以在另一个应用程序域中执行插件,这样就可以 Unload,从而在不关闭宿主的情况下修改插件脚本了。
Server.cs


复制代码
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CSScriptLibrary;

namespace Server
{
    
// 服务器上下文环境,注意 MarshalByRefObject。
    public class MyContent : MarshalByRefObject
    {
        
public int I { getset; }
    }

    
// 插件接口
    public interface IAddin
    {
        
void Execute(MyContent content);
    }

    
class Program
    {
        
static void Main(string[] args)
        {
            var content 
= new MyContent { I = 12345 };

            var asmFile 
= CSScript.Compile("TestAddin.cs");
            
using (var helper = new AsmHelper(asmFile, "AddinDomain"true))
            {
                
// 注意设置依赖程序集搜索路径
                helper.ProbingDirs = new[] { AppDomain.CurrentDomain.BaseDirectory };

                
// 创建代理对象
                var addin = helper.CreateObject("TestAddin"as IAddin;
                
                
// 调用插件方法
                addin.Execute(content);
            }
        }
    }
}
复制代码

 

TestAddin.cs

复制代码
代码
//css_ref Server.exe;
using System;
using Server;

// 插件,注意 MarshalByRefObject。
public class TestAddin : MarshalByRefObject, IAddin
{
    
public void Test()
    {
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
    }

    
public void Execute(MyContent content)
    {
        Console.WriteLine(content.I);
    }
}
复制代码

 

 由于要跨域传递,因此某些地方需要添加 MarshalByRefObject。另外,注意设置 helper.ProbingDirs,否则找不到插件依赖的程序集,抛出异常。雨痕偷懒没有把公用类型放到一个单独的程序集中,正式开发时,建议将其放到一个独立的类库项目中。

CS-Script 的功能很多,用它替代 CodeDom,可以省不少力气。更多细节,请参考其帮助文件。

编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示