【问题标题】:Loading DLLs at runtime in C#在 C# 中运行时加载 DLL
【发布时间】:2013-08-24 02:48:05
【问题描述】:

我试图弄清楚如何在 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();
        }
    }
}

【问题讨论】:

标签: c# reflection dll


【解决方案1】:
foreach (var f in Directory.GetFiles(".", "*.dll"))
            Assembly.LoadFrom(f);

这会加载可执行文件文件夹中的所有 DLL。

在我的例子中,我试图使用Reflection 来查找一个类的所有子类,即使在其他 DLL 中也是如此。这行得通,但我不确定这是否是最好的方法。

编辑:我计时了,它似乎只是第一次加载它们。

Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 4; i++)
{
    stopwatch.Restart();
    foreach (var f in Directory.GetFiles(".", "*.dll"))
        Assembly.LoadFrom(f);
    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);
}

输出: 34 0 0 0

因此可以在任何反射搜索之前运行该代码以防万一。

【讨论】:

    【解决方案2】:

    这并不难。

    您可以检查已加载对象的可用功能,如果您通过名称找到您要查找的功能,则窥探其预期的参数(如果有)。如果是您要查找的调用,则使用 MethodInfo 对象的 Invoke 方法调用它。

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

    这是非常简单的东西。

    【讨论】:

    • 哇,不知道为什么投反对票。在过去的 12 年里,我有一个生产应用程序正是这样做的。 * 耸耸肩 * 任何人都需要一些代码来执行此操作,给我发消息。我将打包我的部分生产代码并发送出去。
    • 我怀疑否决票可能与缺乏示例和浓缩语气有关......不过,您似乎有完整答案的基础,所以不要害怕编辑更多细节:)
    • 说“这是非常简单的东西”有点粗鲁,这就是为什么让你投反对票的原因。
    • 我没有粗鲁或居高临下.... 6 年前。很明显,语气在文本中没有体现出来。它真的很轻松......我也真的觉得这些年来我在那里有一个代码示例的链接,我不知道它去了哪里(假设它真的像我记得的那样在那里)。 :\
    • 我不知道 MethodInfo 是如何工作的,但它似乎很有价值。我想说你的答案有可能比目前接受的更好,但它需要完成。如果你能解决它,将不胜感激。如果是这样,请不要链接到代码示例。这些将来可能会被破坏。最好自己提供示例,并可能提供指向源的链接或额外信息以供继续阅读。
    【解决方案3】:

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

    看起来您来自动态编程语言? C# 肯定不是那样的,你想做的事情会很困难。

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

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

    type.GetMethod("Output").Invoke(c, null); 之类的东西应该这样做。

    【讨论】:

      【解决方案4】:

      您需要创建一个公开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();
           }
      

      【讨论】:

      • 非常感谢 - 这正是我正在寻找的。我不敢相信这没有比其他答案更高,因为它显示了动态关键字的使用。
      • 啊,现在我看到它也在 DarkFalcon 的回答中。不过,你的更短,更容易看到。 :)
      【解决方案5】:

      现在,您正在创建程序集中定义的每个类型的实例。您只需创建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();
          }
      }
      

      【讨论】:

        【解决方案6】:

        成员必须在编译时可解析才能直接从 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();
                }
            }
        }
        

        【讨论】:

        • 请注意,这会尝试在程序集中的每种类型上调用Output,这可能会在找到“正确”类之前抛出......
        • @ReedCopsey,同意,但对于他的简单示例,他的类型是唯一可见的。 “在程序集之外唯一可见的类型是公共类型和嵌套在其他公共类型中的公共类型。”对于一个重要的例子,显然这将是一个问题......
        • 这两个例子很好! :)
        • 这就是为什么经常使用接口的原因,你可以做特征检测例如IDog dog = someInstance as IDog; 并测试它是否不为空。将您的接口放在客户端共享的公共 DLL 中,任何将动态加载的插件都必须实现该接口。然后,这将允许您针对 IDog 接口对客户端进行编码,并在编译时进行智能感知+强类型检查,而不是使用动态。
        • @Tarek.Mh:这需要在编译时依赖于Class1。此时您可以使用new Class1()。询问者明确指定了运行时依赖项。 dynamic 允许程序根本不需要对 Class1 的编译时依赖。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多