我正在开发一个 C# 应用程序,该应用程序应该检查其他 C# 可执行文件并通过单元测试断言它们公开的接口的某些属性。(就上下文而言,这是一个 CS 课程的评分应用程序;任务是从文本文件中解析数字范围并根据固定接口返回它们。)
到目前为止,我已经设法:
- 将可执行文件作为变量加载
assembly
,使用Assembly.LoadFrom(string)
- 获取相关接口的类型,使用
assembly.GetType(string)
- 找到接口的实现类型,再次使用
assembly.getType(string)
- 将实现类型实例化为
dynamic
对象,使用type.GetConstructor(Type[])
andconstructor.Invoke(Object[])
此时,我有一个dynamic
对象loader
,我知道它实现了我正在测试的接口。我想调用其中一个接口方法obj
,所以我运行:
dynamic rangeSet = loader.GetRangeSetFromFile (inputFile); // inputFile is a string
这将引发InvalidCastException
以下跟踪:
System.InvalidCastException : Cannot cast from source type to destination type.
at SwapAssignment3.Implementations.RangeLoaderAdapter.GetRangeSetFromFile (string) <IL 0x0001e, 0x00066>
at (wrapper dynamic-method) object.CallSite.Target (System.Runtime.CompilerServices.Closure,System.Runtime.CompilerServices.CallSite,object,string) <IL 0x00036, 0x0007b>
at System.Dynamic.UpdateDelegates.UpdateAndExecute2<object, string, object> (System.Runtime.CompilerServices.CallSite,object,string) <0x003cf>
at AssignmentTests.R3Test.TestLoadingViaInterface () [0x00054] in /Users/tim/Dropbox/Courses/CSSE375-TA/AssignmentTests/AssignmentTests/R3Test.cs:82
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) <0x00003>
at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) <IL 0x000db, 0x00147>
为什么会抛出这个错误?运行有问题的可执行文件本身就可以正常工作;此错误仅在我的测试应用程序的上下文中引发。该GetRangeSetFromFile
方法的主体如下:
public IRangeSet GetRangeSetFromFile(string filePath)
{
var newRange = new RangeStorage();
_fileProcessor.ProcessFile(filePath);
newRange.StoreElements((List<IRange>) _fileProcessor.GetStorage());
return newRange;
}
我有充分的理由相信(从程序输出中,除其他外)演员错误是从第三行引发的,与List<IRange>
演员; 但是,由于跟踪提供了 IL 位置,因此我不能 100% 确定这一点,而且我不知道为什么该演员一开始会失败,因为如果程序自行运行(在我的测试员)。
我的主要问题是:为什么会引发这个演员错误,我该如何避免它?
编辑:根据要求,测试代码如下:
Type interfaceType = assembly.GetType("IRangeLoader");
List<Type> implementingTypes = new List<Type> (assembly.GetTypes ())
.FindAll ((Type t) => t.IsClass)
.FindAll ((Type t) => (new List<Type> (t.GetInterfaces ())).Contains (interfaceType));
Type implementingType = implementingTypes[0];
ConstructorInfo ctor = implementationType.GetConstructor (new Type[] {});
dynamic loader = ctor.Invoke (new Object[] {});
dynamic rangeSet = loader.GetRangeSetFromFile ("sample range.txt");