在C#中在运行时加载DLL


91

我试图弄清楚如何在C#应用程序内部在运行时导入和使用.dll。使用Assembly.LoadFile(),我设法使程序加载dll(这部分肯定可以正常工作,因为我能够使用ToString()获得类的名称),但是我无法使用“输出”控制台应用程序内部的方法。我正在编译.dll,然后将其移动到控制台项目中。在CreateInstance和能够使用这些方法之间是否还有其他步骤?

这是我的DLL中的类:

namespace DLL
{
    using System;

    public class Class1
    {
        public void Output(string s)
        {
            Console.WriteLine(s);
        }
    }
}

这是我要加载DLL的应用程序

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}

Answers:


128

成员在编译时必须是可解析的,才能直接从C#调用。否则,您必须使用反射或动态对象。

反射

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                type.InvokeMember("Output", BindingFlags.InvokeMethod, null, c, new object[] {@"Hello"});
            }

            Console.ReadLine();
        }
    }
}

动态(.NET 4.0)

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                dynamic c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}

12
请注意,这会尝试调用程序Output集中的每种类型,这可能会在找到“正确的”类之前抛出……
Reed Copsey 2013年

1
@ReedCopsey,同意,但是对于他的简单示例,他的类型是唯一可见的类型。“程序集外部唯一可见的类型是公共类型和嵌套在其他公共类型中的公共类型。” 对于一个不平凡的例子,显然这将是一个问题……
Dark Falcon

1
用两个例子整齐!:)
Niels Abildgaard 2014年

22
这就是为什么经常使用接口的原因,并且您可以执行功能检测,例如,IDog dog = someInstance as IDog;并测试它是否不为null。将接口放在客户端共享的通用DLL中,任何将动态加载的插件都必须实现该接口。然后,这将使您可以根据IDog接口对客户端进行编码,并在编译时进行intellisense + strong类型检查,而不是使用动态检查。
AaronLS 2014年

1
@ Tarek.Mh:那将需要在编译时依赖Class1。那时您可以使用new Class1()。询问者明确指定了运行时依赖项。dynamic使程序完全不需要编译时依赖Class1
黑暗猎鹰

39

现在,您正在创建Assembly中定义每种类型的实例。您只需要创建一个实例Class1即可调用该方法:

class Program
{
    static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var theType = DLL.GetType("DLL.Class1");
        var c = Activator.CreateInstance(theType);
        var method = theType.GetMethod("Output");
        method.Invoke(c, new object[]{@"Hello"});

        Console.ReadLine();
    }
}

19

您需要创建一个暴露该Output方法的类型的实例:

static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var class1Type = DLL.GetType("DLL.Class1");

        //Now you can use reflection or dynamic to call the method. I will show you the dynamic way

        dynamic c = Activator.CreateInstance(class1Type);
        c.Output(@"Hello");

        Console.ReadLine();
     }

非常感谢您-这正是我想要的。我不敢相信这没有比其他答案高,因为它显示了dynamic关键字的使用。
skiphoppy16年

啊,现在我也看到了DarkFalcon的回答。不过,您的广告更短,更容易看清。:)
skiphoppy

0

Activator.CreateInstance() 返回一个没有Output方法的对象。

看起来您来自动态编程语言?C#绝对不是那样,而您尝试执行的操作将很困难。

由于您是从特定位置加载特定的dll,也许您只想将其添加为对控制台应用程序的引用?

如果您绝对要通过加载程序集Assembly.Load,则必须通过反射来调用任何成员。c

type.GetMethod("Output").Invoke(c, null);这样的事情应该做。


-1

没那么难。

您可以检查已加载对象的可用功能,如果找到了要按名称查找的对象,则可以监听其预期的参数(如果有)。如果这是您要查找的调用,请使用MethodInfo对象的Invoke方法进行调用。

另一个选择是简单地将外部对象构建到接口,然后将加载的对象转换为该接口。如果成功,则本地调用该函数。

这是非常简单的东西。


哇,不知道为什么投票失败。在过去的12年中,我有一个生产应用程序正是在这样做。*耸耸肩*任何人都需要一些代码来做到这一点,给我发消息。我将打包部分生产代码并将其发送。
ChrisH '16

9
我怀疑投票否决与缺乏示例和简洁的语气有关……好像您有完整答案的基础,所以请不要害怕编辑更多细节:)
Shadow

1
说“这是很简单的东西”是很不礼貌的,这就是为什么让您失望。
ABPerson

1
6年前,我没有粗鲁或屈尊。显然,音调不会以文字形式出现。真的本来是想放松一下……我也真的觉得这些年来我一直在那里有一个代码示例的链接,而且我也不知道它去了哪里(假设它确实存在,就像我记得的那样) )。:\
ChrisH

我不知道MethodInfo的工作原理,但似乎很有价值。我想说,您的答案可能会比目前接受的答案更好,但需要完成。如果您熟悉它,将不胜感激。如果是这样,请不要链接到代码示例。这些将来可能会被破坏。最好自己提供示例,并可能提供源链接或其他信息以供继续阅读。
SpaghettiCook
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.