在C#项目中把可复用的逻辑编译成DLL(动态链接库),可以让多个应用程序共享同一份代码,也便于团队分工维护。下面直接说明从建库到调用的完整过程。
一、创建C#类库项目
打开Visual Studio,选择“创建新项目”,在项目模板中搜索“类库”,注意要选择“类库(.NET Framework)”或“类库(.NET Core/.NET)”对应你实际运行环境。类库项目编译后不会产生可执行文件,而是输出一个.dll文件。
新建类库后,默认会生成一个Class1.cs。我们把它改成一个有实际意义的计算工具类,并标记为public,否则外部程序无法引用其中的成员。以下代码演示了一个简单的数学工具类库。
// 数学工具类库,编译后生成 MathLib.dll
using System;
namespace MathLib
{
public class Calculator
{
// 求和
public int Add(int a, int b)
{
return a + b;
}
// 获取当前时间字符串
public string NowString()
{
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
}
}
在解决方案配置中选择“Release”模式,右键项目点击“生成”,Visual Studio会在输出目录(如binReleasenet6.0)下生成MathLib.dll。这个文件就是动态链接库。
需要注意,类库的目标框架(Target Framework)必须与调用方保持一致或兼容。例如调用程序是.NET 6,类库也应该是.NET 6或.NET Standard 2.0,否则添加引用时会提示框架不兼容。
二、在C#项目中引用并调用DLL
新建一个控制台应用作为调用方,在“解决方案资源管理器”中右键“依赖项”或“引用”,选择“添加项目引用”可直接引用同一解决方案下的类库;如果是外部DLL,则选择“浏览”找到MathLib.dll文件。
添加引用后,在代码中通过using引入命名空间即可使用。下面代码展示了控制台程序如何调用刚才生成的DLL中的方法。
using System;
using MathLib; // 引入DLL中的命名空间
class Program
{
static void Main()
{
Calculator calc = new Calculator();
int result = calc.Add(3, 5);
Console.WriteLine("3 + 5 = " + result);
Console.WriteLine("当前时间:" + calc.NowString());
}
}
编译并运行控制台程序,若输出结果为“3 + 5 = 8”及时间字符串,说明DLL调用成功。这种方式的优势在于,若后续Calculator逻辑变更,只需替换MathLib.dll而不用重新编译主程序(在接口不变的前提下)。
如果DLL还依赖其他第三方库,那些依赖的.dll也要一同拷贝到主程序输出目录,否则运行时会抛出“未能加载文件或程序集”的异常。
三、通过反射动态加载DLL
除了静态添加引用,C#还支持在运行时通过反射加载DLL,这在插件化架构中很常见。使用Assembly.LoadFrom方法可以指定路径加载,再创建实例并调用方法。
以下示例展示如何从指定路径动态加载MathLib.dll并调用Add方法,无需在编译期引用。
using System;
using System.Reflection;
class DynamicLoader
{
static void Run()
{
// 加载位于程序目录下的DLL
Assembly asm = Assembly.LoadFrom("MathLib.dll");
Type type = asm.GetType("MathLib.Calculator");
object instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("Add");
int r = (int)method.Invoke(instance, new object[] { 10, 20 });
Console.WriteLine("动态调用结果:" + r);
}
}
反射调用的灵活性高,但性能略低于直接引用,且绕过了编译期类型检查,代码健壮性需自行保证。在固定依赖场景中,优先使用静态引用方式。
若希望DLL具备强命名(防止版本冲突、支持放入GAC),需要在项目属性中生成密钥文件(.snk),并对程序集进行签名。强命名后的DLL在被引用时会校验公钥,能有效避免“DLL地狱”问题。
四、常见错误与排查
开发中常遇到“找不到类型或命名空间”的编译错误,多数情况是忘了添加引用或using语句。若运行时报BadImageFormatException,通常是平台目标不匹配,比如主程序是Any CPU但DLL是x86,需在项目属性中统一平台目标。
另一个易错点是把内部类(internal)当作public暴露,外部工程无法访问。务必确认需要导出的类型和方法都标有public修饰符,且所在程序集未设置InternalsVisibleTo限制。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| CS0246 找不到类型 | 未添加引用或using | 添加DLL引用并写using |
| FileNotFoundException | DLL未拷贝到输出目录 | 检查生成事件或手动复制 |
| BadImageFormatException | 平台目标不一致 | 统一x86/x64/Any CPU |
掌握上述创建、引用、动态加载与排错方法后,你就能在C#中规范地使用DLL来拆分和复用代码模块,提升工程的可维护性与部署效率。