You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行时动态加载程序集:如何避免类定义重复的技术咨询

问题描述

我构建了一套较为复杂的类库系统,希望其他开发者能够加载其中一个程序集,读取其属性、调用其方法并监听其事件。目前,终端开发者必须引用Library1,同时还要搭配Library1所依赖的其他所有类库。

为解决该问题,我计划制作一个安装程序,将所有类库复制到指定位置,终端开发者只需引用MainLibrary,由它动态加载其余类库并暴露所需使用的内容。

Assembly library1Assembly = Assembly.LoadFrom("/path/to/where/the/libraries/where/installed/library1.dll");

Type? library1Type = library1Assembly.GetType("Library1.MainClass");

dynamic library1Instance = Activator.CreateInstance(library1Type);

不过Library1包含一些希望开发者能够访问的公共类,例如User类。MainLibrary需要暴露Library1已有的List<User> Users和User SelectedUser这两个属性,因此我需要创建一个与Library1.User完全一致的MainLibrary.User类,其他多个类、自定义EventArgs等也需如此处理。

我并不希望这种重复定义的做法,请问是否有其他实现方式?

解决方案

1. 提取共享契约程序集

把User、自定义EventArgs等需要跨库共享的类型,单独提取到一个独立的契约程序集(比如Library.Contracts.dll)中:

  • 让Library1和MainLibrary都引用这个契约程序集
  • 终端开发者只需引用MainLibrary和契约程序集,就能直接使用这些共享类型,无需重复定义
  • 动态加载Library1后,直接将实例转换为契约程序集中的接口或类型,暴露给开发者:
// 契约程序集中定义共享接口与类型
public interface IUser { 
    string Name { get; set; }
    int Age { get; set; }
}
public interface IMainClass {
    List<IUser> Users { get; }
    IUser SelectedUser { get; set; }
    event EventHandler<CustomUserEventArgs> UserSelected; 
}
public class CustomUserEventArgs : EventArgs {
    public IUser TargetUser { get; set; }
}

// MainLibrary中动态加载并转换
Assembly library1Assembly = Assembly.LoadFrom("path/to/library1.dll");
Type library1Type = library1Assembly.GetType("Library1.MainClass");
IMainClass instance = (IMainClass)Activator.CreateInstance(library1Type);
// 将IMainClass实例暴露给开发者,直接使用契约类型即可

2. 使用动态类型配合扩展方法

如果不想额外增加契约程序集,可以用dynamic类型配合扩展方法,让开发者无需直接引用Library1就能操作共享类型:

// MainLibrary中提供扩展方法封装类型操作
public static class UserExtensions {
    public static string GetUserName(this dynamic user) {
        return user.Name;
    }

    public static void SetUserAge(this dynamic user, int age) {
        user.Age = age;
    }
}

// 开发者使用示例
dynamic selectedUser = mainLibraryInstance.SelectedUser;
string userName = selectedUser.GetUserName();
selectedUser.SetUserAge(30);

这种方式无需重复定义类型,但缺点是缺少编译时类型检查,依赖运行时保证属性/方法存在。

3. 全封装反射访问逻辑

把对Library1中类型的所有访问逻辑封装在MainLibrary内部,开发者仅通过MainLibrary提供的方法、属性操作,完全不接触底层类型:

// MainLibrary中封装所有对外接口
public class MainLibraryWrapper {
    private dynamic _library1Instance;

    public MainLibraryWrapper() {
        // 动态加载Library1的逻辑
        Assembly library1Assembly = Assembly.LoadFrom("path/to/library1.dll");
        Type library1Type = library1Assembly.GetType("Library1.MainClass");
        _library1Instance = Activator.CreateInstance(library1Type);
    }

    // 封装Users属性,返回只读枚举
    public IEnumerable<object> Users => _library1Instance.Users;

    // 封装SelectedUser的属性访问
    public string SelectedUserName => _library1Instance.SelectedUser.Name;
    public void SetSelectedUserAge(int age) => _library1Instance.SelectedUser.Age = age;

    // 封装事件监听
    public event EventHandler<EventArgs> UserSelected {
        add => _library1Instance.UserSelected += value;
        remove => _library1Instance.UserSelected -= value;
    }
}

这种方式完全隔离了底层类库,开发者无需知道Library1的存在,但封装工作量较大,适合需要严格控制访问权限的场景。

内容的提问来源于stack exchange,提问作者Claudio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 07:15:26