我可以在运行时加载 .NET 程序集并实例化只知道名称的类型吗?

Posted

技术标签:

【中文标题】我可以在运行时加载 .NET 程序集并实例化只知道名称的类型吗?【英文标题】:Can I load a .NET assembly at runtime and instantiate a type knowing only the name? 【发布时间】:2010-10-02 16:53:17 【问题描述】:

如果我只有 DLL 名称和类名,而不在项目中添加对程序集的引用,是否可以在运行时实例化对象?这个类实现了一个接口,所以一旦我实例化了这个类,我就会把它转换成接口。

程序集名称:

库.dll

类型名称:

公司.项目.类名


编辑:我没有 DLL 的绝对路径,所以 Assembly.LoadFile 不起作用。 DLL 可能位于应用程序根目录、system32 中,甚至加载到 GAC 中。

【问题讨论】:

【参考方案1】:

是的。您需要使用Assembly.LoadFrom 将程序集加载到内存中,然后您可以使用Activator.CreateInstance 创建您首选类型的实例。您需要先使用反射查找类型。这是一个简单的例子:

Assembly assembly = Assembly.LoadFrom("MyNice.dll");

Type type = assembly.GetType("MyType");

object instanceOfMyType = Activator.CreateInstance(type);

更新

当您拥有程序集文件名和类型名称时,您可以使用Activator.CreateInstance(assemblyName, typeName) 要求 .NET 类型解析将其解析为类型。您可以使用 try/catch 对其进行包装,这样如果失败,您可以搜索目录,您可以在其中专门存储其他程序集,否则这些程序集可能不会被搜索。这将在此时使用前面的方法。

【讨论】:

我没有 dll 的绝对路径,所以 assemlby.LoadFile 等。行不通,还有其他想法吗? @MegaByte:LoadFrom 与 LoadFile 不同。它将解析您的依赖关系,它应该从已知路径(GAC、exe 目录等)解析 DLL 名称。有关详细信息,请参阅 MSDN。 @MegaByte:使用 Directory.GetCurrentDirectory() 获取。 还有一件事...(又是我)嗯,你不能只用“MyType”作为类型名称,它必须跟在 NAMESPACE 后面。所以这会更准确:Type type = assembly.GetType("MyNamespace"+"."+"MyType"); @Cipi:从技术上讲,类型是它的完整命名空间名称(命名空间的概念是一种语言便利)。您可以在 CLR 中拥有一个没有命名空间的类型 - 我只是提供了一个过于简化的示例。【参考方案2】:

考虑不同Load* 方法的局限性。来自MSDN 文档...

LoadFile 不会将文件加载到 LoadFrom 上下文中,并且不会像 LoadFrom 方法那样使用加载路径解析依赖项

有关加载上下文的更多信息,请参阅 LoadFrom 文档。

【讨论】:

【参考方案3】:

Activator.CreateInstance 应该可以工作。

IFace object = (IFace)Activator.CreateInstance( "AssemblyName",
                                                "TypeName" )
                               .Unwrap();

注意:类型名称必须是完全限定类型。

例子:

var aray = (IList)Activator.CreateInstance("mscorlib","System.Collections.ArrayList").Unwrap();
aray.Add(10);

foreach (object obj in aray)

    Console.WriteLine(obj);

【讨论】:

请注意:TypeName 必须是完全合格的。我不得不这样称呼它:Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") 这会返回一个ObjectHandle。要进入您的界面,您需要执行ObjectHandle.UnWrap()【参考方案4】:

我发现这个问题和一些答案非常有用,但是我确实遇到了路径问题,所以这个答案将涵盖通过查找 bin 目录路径来加载库。

第一个解决方案:

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFrom(assemblyPath);
Type T = assembly.GetType("Company.Project.Classname");
Company.Project.Classname instance = (Company.Project.Classname) Activator.CreateInstance(T);

第二个解决方案

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFile(assemblyPath);
(Company.Project.Classname) instance = (Company.Project.Classname) assembly.CreateInstance("Company.Project.Classname");

您可以对接口使用相同的原则(您将创建一个类但要转换为接口),例如:

(Company.Project.Interfacename) instance = (Company.Project.Interfacename) assembly.CreateInstance("Company.Project.Classname");

此示例适用于 Web 应用程序,但类似的可用于桌面应用程序,只是路径以不同的方式解析,例如

Path.GetDirectoryName(Application.ExecutablePath)

【讨论】:

【参考方案5】:

这很简单。

来自 MSDN 的示例:

public static void Main()

    // Use the file name to load the assembly into the current
    // application domain.
    Assembly a = Assembly.Load("example");
    // Get the type to use.
    Type myType = a.GetType("Example");
    // Get the method to call.
    MethodInfo myMethod = myType.GetMethod("MethodA");
    // Create an instance.
    object obj = Activator.CreateInstance(myType);
    // Execute the method.
    myMethod.Invoke(obj, null);

这是一个参考链接

https://msdn.microsoft.com/en-us/library/25y1ya39.aspx

【讨论】:

这是一种支持动态加载代码的可怕方式。 MS 一直喜欢强迫我们进入太多细节。【参考方案6】:

从 Framework v4.5 开始,您可以使用 Activator.CreateInstanceFrom() 轻松实例化程序集中的类。 下面的例子展示了如何使用它以及如何调用一个传递参数和获取返回值的方法。

    // Assuming moduleFileName contains full or valid relative path to assembly    
    var moduleInstance = Activator.CreateInstanceFrom(moduleFileName, "MyNamespace.MyClass");
    MethodInfo mi = moduleInstance.Unwrap().GetType().GetMethod("MyMethod");
    // Assuming the method returns a boolean and accepts a single string parameter
    bool rc = Convert.ToBoolean(mi.Invoke(moduleInstance.Unwrap(), new object[]  "MyParamValue"  ));

【讨论】:

【参考方案7】:
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");

Type type = assembly.GetType("MyType");

dynamic instanceOfMyType = Activator.CreateInstance(type);

因此,通过这种方式,您可以使用不获取methodinfo的函数,然后调用它。您将这样做 instanceOfMyType.MethodName(); 但是你不能使用 Intellisense,因为动态类型是在运行时而不是在编译时输入的。

【讨论】:

【参考方案8】:

是的。我现在没有任何我亲自做过的例子。稍后我找到一些后会发布。基本上,您将使用反射来加载程序集,然后提取您需要的任何类型。

与此同时,此链接应该可以帮助您入门:

Using reflection to load unreferenced assemblies at runtime

【讨论】:

【参考方案9】:
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();

【讨论】:

【参考方案10】:

您可以使用 *Assembly.Load** 方法加载程序集。使用 Activator.CreateInstance 您可以创建所需类型的新实例。请记住,您必须使用要加载的类的完整类型名称(例如 Namespace.SubNamespace.ClassName)。使用 Type 类的方法 InvokeMember,您可以调用该类型的方法。

另外,考虑到一旦加载,程序集在整个 AppDomain 也被卸载之前无法卸载(这基本上是内存泄漏)。

【讨论】:

【参考方案11】:

根据这种功能对您的项目的内在程度,您可能需要考虑像 MEF 这样的东西,它会为您处理组件的加载和绑定。

【讨论】:

【参考方案12】:

是的,是的,您将要在 Assembly 类上使用静态 Load 方法,然后调用 Load 调用返回给您的 Assembly 实例上的 CreateInstance 方法。

此外,您可以根据需要在 Assembly 类上调用以“Load”开头的其他静态方法之一。

【讨论】:

【参考方案13】:

你可以这样做:

using System.Reflection;

Assembly MyDALL = Assembly.Load("DALL"); //DALL name of your assembly
Type MyLoadClass = MyDALL.GetType("DALL.LoadClass"); // name of your class
 object  obj = Activator.CreateInstance(MyLoadClass);

【讨论】:

【参考方案14】:

我正在使用 .net5(.net 核心 5)。我需要做的是:

    static void Main()
    
        //...

        AppDomain.CurrentDomain.AssemblyResolve += CciBaseResolveEventHandler;

        Application.Run(new FormMain());
    

    private static Assembly CciBaseResolveEventHandler(object sender, ResolveEventArgs args)
    
        return Assembly.LoadFile($@"Directory.GetCurrentDirectory()\nameof(CciBase).dll");
    
    将“CciBase”更改为您的 dll 名称。 确保 dll 与 exe 位于同一文件夹中。 在主进程启动之前执行此操作 (Application.Run())。

【讨论】:

以上是关于我可以在运行时加载 .NET 程序集并实例化只知道名称的类型吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 blazor 加载外部 .NET Standard 2.0 程序集

处理非常大的数据集并及时加载

Reflection(反射)

C#高阶-反射

并行化只需要在每 X 次迭代中运行的慢速函数,以免降低循环速度

反射实例及概念