4. C#适配器工作原理
在 TestStand 中,C# 适配器并不是把 C# 代码“翻译”成 TestStand 序列,而是通过 .NET 运行时把已编译的程序集加载进测试进程,再借助反射与委托机制调用其中的方法。理解这条链路,才能解释为什么有时序列能正常执行、有时却报“找不到方法”或“类型不匹配”。本节围绕程序集加载、方法调用、参数传递、返回值与异常处理,以及程序集路径与版本管理展开。
一、程序集加载:从路径到 AppDomain
当 TestStand 执行一个 C# 步骤时,适配器首先需要定位程序集文件(.dll 或 .exe)。TestStand 默认使用步骤中配置的“Assembly Path”字段,该字段可以是绝对路径,也可以是相对于搜索路径的相对路径。适配器会按以下顺序解析:先检查步骤中显式指定的路径;若为相对路径,则在 TestStand 的搜索目录(如序列文件所在目录、<TestStand>\Bin、系统 PATH)中查找;最后才尝试从 GAC 或已加载的程序集缓存中匹配。
找到文件后,适配器通过 Assembly.LoadFrom 或 Assembly.Load 将其加载到当前 AppDomain。这里有一个关键点:TestStand 的 C# 适配器通常运行在独立的 AppDomain 中,以便隔离不同程序集的版本冲突。加载成功后,适配器会扫描程序集中的公共类型,并缓存类型名、方法名、参数签名等信息,供后续步骤快速匹配。若程序集依赖其他 DLL,CLR 会按“应用程序基目录 → 私有路径 → GAC”的顺序解析依赖;依赖缺失时,加载阶段就会抛出 FileNotFoundException。
二、方法调用与参数传递
程序集加载完成后,适配器根据步骤中配置的“Class Name”和“Method Name”定位目标方法。若方法有重载,适配器会依据参数个数与类型进行匹配;匹配失败时,步骤会报“Method not found”。调用时,TestStand 的参数表达式先被求值为 .NET 可识别的类型,再按方法签名顺序传入。
参数传递需要注意三点。第一,值类型与引用类型的区别:int、double、bool 等值类型按值传递,方法内修改不影响 TestStand 变量;而数组、对象等引用类型传递的是引用,方法内修改元素会影响原对象。第二,out 与 ref 参数:TestStand 适配器支持将 out 参数映射为步骤的输出属性,ref 参数则要求传入的变量可写。第三,可选参数与默认值:若方法定义了默认值,步骤中可省略对应实参,适配器会使用默认值填充。
下面是一个典型的 C# 方法签名与 TestStand 参数映射示例:
// C# 被测方法
public class VoltageTest
{
public bool Measure(double target, out double measured, double tolerance = 0.05)
{
measured = ReadAdc(); // 假设的硬件读取
return Math.Abs(measured - target) <= tolerance;
}
}
在 TestStand 步骤中,target 对应一个数值型参数表达式(如 Locals.TargetVoltage),measured 对应一个输出变量(如 Locals.MeasuredVoltage),tolerance 可省略或显式传入。方法返回的 bool 会写入步骤的“Result”属性,供后续判断使用。
三、返回值与异常处理
返回值处理相对直接:适配器把 .NET 返回值转换为 TestStand 属性类型。数值、字符串、布尔值可直接映射;数组和对象则转换为 TestStand 的容器或对象引用。若方法返回 void,步骤的返回值属性为空,但仍可通过 out 参数获取结果。
异常处理是 C# 适配器最容易出问题的环节。当目标方法抛出异常时,CLR 会将其包装为 TargetInvocationException,适配器再将其解包为 TestStand 的步骤错误。常见异常与对应现象如下:
- FileNotFoundException:程序集或依赖 DLL 未找到,检查 Assembly Path 与依赖部署。
- BadImageFormatException:程序集位数与 TestStand 进程不匹配(如 x64 程序集加载到 x86 进程)。
- TypeLoadException / MissingMethodException:类名或方法名拼写错误,或版本不匹配导致签名变化。
- FormatException / InvalidCastException:参数表达式类型与形参类型不兼容,需检查表达式类型或增加转换。
建议在 C# 方法内部对可预期错误进行捕获,并返回明确的错误码或错误信息,而不是直接抛出异常。这样 TestStand 步骤可以基于返回值做分支判断,而不是依赖异常中断序列。例如:
public int MeasureSafe(double target, out double measured)
{
try
{
measured = ReadAdc();
return Math.Abs(measured - target) <= 0.05 ? 0 : 1;
}
catch (Exception ex)
{
measured = double.NaN;
return -1; // -1 表示硬件异常
}
}
四、程序集路径与版本管理
程序集路径管理直接影响部署的可移植性。推荐做法是:将程序集放在序列文件同目录或子目录下,步骤中使用相对路径;若必须使用绝对路径,应在部署文档中明确记录。TestStand 的搜索路径可通过 StationGlobals 或序列文件属性配置,避免硬编码到每台机器。
版本管理方面,.NET 程序集若具有强名称(Strong Name),CLR 会严格按版本号加载。当 TestStand 序列引用了 1.0.0.0 版本,而部署环境只有 1.1.0.0 时,会抛出 FileLoadException。解决方法有两种:一是使用 bindingRedirect 在配置文件中将旧版本重定向到新版本;二是将程序集放入 GAC 并确保版本号一致。对于频繁迭代的测试程序集,建议关闭强名称或使用固定版本号,减少部署摩擦。
总结来说,C# 适配器的工作链路是“路径解析 → 程序集加载 → 反射匹配 → 参数转换 → 方法执行 → 返回值/异常回传”。掌握每个环节的输入、输出与失败模式,就能在序列报错时快速定位是路径、版本、签名还是参数类型的问题,从而把 C# 代码稳定地集成进 TestStand 测试序列。