我在使用程序集和DLL时遇到了一些麻烦。
instrument_被声明为一个对象,我从dll创建一个“PP150”实例,其路径由path _指定。
string className = ContineoProperties.getSingleton().getClassName(path_);
assembly_ = Assembly.LoadFrom(path_);
Type classType = assembly_.GetType("Instrument." + className);
instrument_ = Activator.CreateInstance(classType);
稍后我要调用方法isntrument_.instrumentCommand(cmd.getCommandName())
我得到的错误是在我调用方法时。
'object'不包含'instrumentCommand'的定义
isntrument_被创建得很好。它只是给我一个问题的方法调用。该方法确实存在于“PP150.dll”中。我是否需要一些DLLImport才能将其识别为函数?
谢谢, P
答案 0 :(得分:3)
如果在编译时不知道对象类型,
要调用在对象上定义的方法,必须使用Reflection。
MethodInfo mInfo = classType.GetMethod("instrumentCommand");
mInfo.Invoke(instrument_, new Object[] { _parameters});
答案 1 :(得分:2)
编译器永远不会识别您通过反射加载的类型上的方法(例如,使用Assembly.GetType()
和Activator.CreateInstance()
)。除非您在构建时具有可用的类型元数据,否则如果您尝试调用未在Object本身上定义的方法,则始终会收到该错误。
您可以使用两种方法进行此类方法调用。它们都要求你放弃类型安全,唯一的区别是所需的工作量。在这两种情况下,如果您犯了错误,编译器将不告诉您 - 您将获得运行时异常。
将instrument_声明为dynamic
而不是object
。显然,这只适用于.NET 4.0,但它完全符合您的要求。方法调用将在运行时调度,因此只要instrument_ references 实例具有具有适当名称的方法调用的实例,它就会起作用。
使用反射来调用方法。你已经使用反射来加载类型了,所以你就在那里。你需要添加这样的东西:
// The array of types is the parameter list; assuming instrumentCommand takes
// a string it would look like this:
MethodInfo method = classType.GetMethod("instrumentCommand", new Type[] { typeof(string) });
method.Invoke(instrument_, new object[] { cmd.getCommandName() });
答案 2 :(得分:1)
这是因为Activator.CreateInstance
返回object
。我将为interface
创建一个单独的DLL,它由您要实例化的类实现。包含此类的DLL和可执行文件都应引用包含该接口的DLL。这样您就可以将object
返回的Activator.CreateInstance
转换为接口,并调用其方法:
IInstrument.dll:
interface IInstrument
{
void instrumentCommand(string cmd);
}
Instrument.dll(添加IInstrument.dll作为参考):
class Instrument : IInstrument
{
public void instrumentCommand(string cmd)
{
// ... implementation ...
}
}
InstrumentApp.exe(添加IInstrument.dll作为参考):
class Program
{
public static void Main()
{
// ... load Instrument.dll into assembly object ...
// ... load the type from the assembly ...
IInstrument instrument_ = (IInstrument)Activator.CreateInstance(classType);
instrument_.instrumentCommand(cmd.getCommandName());
}
}
答案 3 :(得分:1)
最简单的方法是再次链接PP150。
如果您确实链接了dll,则必须使用Assembly.LoadFile或Assembly.Load而不是LoadFrom,因为最后一个将导致程序集加载在LoadFrom加载程序上下文中加载程序集,这将改变类型标识。 假设你通过LoadFrom从程序集A加载类型T,并且你也链接到A.
object CreateTypeFrom()
{
var A = Assembly.LoadFrom(@"xxxx");
return A.CreateInstance("T");
}
void Test()
{
object t = CreateTypeFrom();
T RealT = new T(); // no prob
T Castedt = (T)t; // this will throw an InvalidCastException
T isNull = t as T; // this will result in a null instance
}
正如你所看到的,尽管你确实创建了两次T的实例,但是由于不同的加载器上下文而导致它们无法转换,这会使该类型变得毫无用处。
要摆脱这些东西,你可以简单地使用Reflection来创建一个代理类型,它将你的调用转发给代理类型。如果您使用的是.NET 4,则可以利用DLR在运行时找到最佳匹配方法。下面的代码创建一个Version对象并将其作为动态对象返回。然后我将Major属性调用为整数并将其打印到控制台。如果您使用的是.NET 4或更高版本,则无需例外,也无法编译时错误。
dynamic CreateTypeFrom()
{
var assembly = typeof(string).Assembly;
return assembly.CreateInstance("System.Version", true, BindingFlags.CreateInstance, null, new object[] { 1, 2, 3, 4 }, null, null);
}
[TestMethod]
public void Test()
{
var t = CreateTypeFrom();
int major = t.Major;
Console.WriteLine(major);
}