从DLL实例化的对象调用方法

时间:2011-12-15 21:29:22

标签: c#

我在使用程序集和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

4 个答案:

答案 0 :(得分:3)

如果在编译时不知道对象类型,

要调用在对象上定义的方法,必须使用Reflection。

MethodInfo mInfo = classType.GetMethod("instrumentCommand");

mInfo.Invoke(instrument_, new Object[] { _parameters});

答案 1 :(得分:2)

编译器永远不会识别您通过反射加载的类型上的方法(例如,使用Assembly.GetType()Activator.CreateInstance())。除非您在构建时具有可用的类型元数据,否则如果您尝试调用未在Object本身上定义的方法,则始终会收到该错误。

您可以使用两种方法进行此类方法调用。它们都要求你放弃类型安全,唯一的区别是所需的工作量。在这两种情况下,如果您犯了错误,编译器将告诉您 - 您将获得运行时异常。

  1. 将instrument_声明为dynamic而不是object。显然,这只适用于.NET 4.0,但它完全符合您的要求。方法调用将在运行时调度,因此只要instrument_ references 实例具有具有适当名称的方法调用的实例,它就会起作用。

  2. 使用反射来调用方法。你已经使用反射来加载类型了,所以你就在那里。你需要添加这样的东西:

    // 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);
        }