C# 反射 - 如何在运行时重新加载类? [英] C# Reflection - How to reload a class on runtime?

查看:33
本文介绍了C# 反射 - 如何在运行时重新加载类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我正在用 C# 开发一个项目,我必须在其中实现反射.我创建了一个带有 GUI 的 WPF 应用程序.这个 GUI 包含一个包含实现特定接口的所有类名的组合框.具有显示类名的类存在于同一个解决方案中.组合框旁边是一个按钮,用于刷新组合框中的内容.但是,当我运行我的应用程序时,修改实现接口的类名,然后单击该刷新按钮,更改不会显示在组合框中.例如,当我更改一个类名时,它应该显示新的类名而不是旧的.

Currently I am working on a project in C# where I have to implement reflection. I have created a WPF application with a GUI. This GUI contains a combobox which contains all the classnames that implement a specific interface. The classes with the displayed classnames live in the same solution. Next to the combobox is a button to refresh the content in the combobox. However, when I run my application, modify a classname that implements the interface, and click on that refresh button the changes don't show up in the combobox. For example, when I change a classname it should display the new classname in stead of the old one.

我已经提取了我项目的这一部分,以便在一个空的控制台应用程序中对其进行测试.这里我有一个由类实现的接口QuickSortAlgorithm、DynamicSortAlgorithm 和 MergeSortAlgorithm.接下来,我在主类中编写了以下直截了当的代码.

I have extracted this part of my project to test it in an empty console application. Here I have an interface that is implemented by the classes QuickSortAlgorithm, DynamicSortAlgorithm and MergeSortAlgorithm. Next I wrote the following, straight forward code, in my main class.

    public static List<string> AlgoList = new List<string>();

    static void Main(string[] args) {
        RefreshAlgorithms();
        Print();

        Console.WriteLine("
Change a classname and press a key 
");
        Console.ReadKey();

        Print();

        Console.WriteLine("
Press a key to exit the program 
");
        Console.ReadKey();
    }

    private static void RefreshAlgorithms() {
        AlgoList.Clear();
        Type AlgorithmTypes = typeof(IAlgorithms);
        foreach (var type in Assembly.GetCallingAssembly().GetTypes()) {
            if (AlgorithmTypes.IsAssignableFrom(type) && (type != AlgorithmTypes)) {
                AlgoList.Add(type.Name);
            }
        }
    }

    private static void Print() {
        Console.WriteLine("Algorithm classes:");
        foreach (var Algo in AlgoList) {
            Console.WriteLine(Algo);
        }
    }

当我运行应用程序时,会看到打印的类名 QuickSortAlgorithm、DynamicSortAlgorithm 和 MergeSortAlgorithm.但是,如果我更改名称,例如,QuickSortAlgorithm 类到 QuickSortAlgorithmmmm 我希望它在我按下一个键后打印 QuickSortAlgorithmmmmm.然而,情况并非如此,名称QuickSortAlgorithm 仍在显示中.

When I run the application is see the classnames QuickSortAlgorithm, DynamicSortAlgorithm and MergeSortAlgorithm printed. However if I change the name of the, for example, QuickSortAlgorithm class to QuickSortAlgorithmmmmm I would expect it to print QuickSortAlgorithmmmmm once I press a key. However this is not the case and the name QuickSortAlgorithm is still being displayed.

我感觉自己忽略了反射概念中的某些内容.这甚至可以在构建解决方案后完成吗?如果我理解正确,这个概念可以在运行时实现更改.我知道它会使我的应用程序变慢,但我真的很想了解更多关于这个概念的信息.如果有人能解释我做错了什么,我会很高兴.

I get the feeling that I overlook something in the concept of reflection. Can this even be done after building the solution? If I understood correctly this concept makes it possible to implement changes on runtime. I know that it will make my application much slower but I'm really eager to learn more about this concept. If one can explain me what I'm doing wrong I would be very happy.

推荐答案

一旦您将已编译的 .NET 程序集加载到您的应用程序中,您就无法在不重新启动和重建应用程序的情况下对该程序集中的类型进行进一步更改.如果这被允许,那么它可能会导致各种奇怪的行为.例如,假设应用程序有一个 List 填充了 3 个 foos,然后 Foo.Idint 更改为一个<代码>字符串.实时数据应该怎么办?

Once you load a compiled .NET assembly into your application, you can't make further changes to the types in that assembly without restarting and rebuilding the application. If this were allowed, then it could lead to all kinds of weird behavior. For example, imagine if the application had a List<Foo> populated with 3 foos and then Foo.Id were changed from an int to a string. What should happen to that live data?

但是,如果您的应用程序进行反射与被反射的程序集不同,则可以进行设置,以便您可以观察该程序集文件的更改并重新进行反射.关键是放弃 System.Reflection(仅适用于加载的程序集),而是使用 Mono.Cecil 库.

However, if your application doing the reflecting is different than the assembly being reflected over, it is possible to set things up such that you can watch for changes to that assembly file and re-do your reflection. The key is to abandon System.Reflection (which only works on loaded assemblies) and instead use the Mono.Cecil library.

Cecil 读取程序集元数据而不将代码加载到应用程序中,因此它适用于仅反射"用例.当然,它不能做的实际上是调用代码.Cecil API 包含许多与 System.Reflection 的相似之处.例如:

Cecil reads in the assembly metadata without loading the code into the application, so it works well for the "reflection-only" use-case. Of course, what it can't do is actually invoke the code. The Cecil API contains many similarities to System.Reflection. For example:

var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly(Path.Combine(projectDirectory, "bin", "Debug", "Something.dll"));
var controllerTypes = assembly.MainModule.Types.Where(t => t.BaseType?.FullName == "System.Web.Mvc.Controller")
    .ToArray();

另一个注意事项是 .NET Framework(不是 .NET Core)包含可以加载和卸载的 AppDomains 的概念.它们就像一个进程中的 .NET子进程",并且有关于什么可以跨越它们的边界的规则.如果您确实需要重新加载代码并执行它,那么这可能是一个解决方案.

Another note is that .NET Framework (not .NET Core) contains the notion of AppDomains which can be loaded an unloaded. These act like .NET "sub-processes" within the one process and have rules about what can cross their boundaries. If you really need to both reload code and execute it, then this could be a solution.

另一个选项可能是 Roslyn 脚本 API,如果您想动态加载和执行源代码(与编译的程序集相比),它会很有效.

Another option could be the Roslyn scripting API, which would work well if you want to dynamically load and execute source code (vs. compiled assemblies).

这篇关于C# 反射 - 如何在运行时重新加载类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆