99

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

6 回答 6

137

成员必须在编译时可解析才能直接从 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();
        }
    }
}
于 2013-08-21T16:05:47.070 回答
42

现在,您正在为程序集中定义的每种类型创建一个实例。您只需要创建一个实例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();
    }
}
于 2013-08-21T16:08:19.527 回答
21

您需要创建一个公开该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();
     }
于 2013-08-21T16:11:52.790 回答
0

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

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

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

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

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

于 2013-08-21T16:13:26.447 回答
0
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

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

于 2020-11-05T06:24:50.580 回答
-1

这不是那么困难。

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

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

这是很简单的东西。

于 2014-10-06T14:29:20.880 回答