C# MarshalByRefObject 与 AppDomain (From ChatGPT)

本文章内容为作者利用ChatGPT学习相关技术时产生,仅供学习方便参考,
不保证资讯100%正确,如有错误烦请指正。

如果类别继承MarshalByRefObject 可以做什么事情?

当一个类别继承自MarshalByRefObject,它的实例可以跨越应用程式执行在不同的应用程式域(AppDomain)中进行通讯。这个特性可让你在分散式应用程式中使用远端物件通讯(Remote Object Communication)。

举个简单的範例,假设有一个名为RemoteCalculator的类别,它继承自MarshalByRefObject,这个类别提供一些计算的功能。

using System;public class RemoteCalculator : MarshalByRefObject{    public int Add(int a, int b)    {        return a + b;    }}

接下来,我们可以在主应用程式域中建立一个RemoteCalculator的实例,然后将它传递给另一个应用程式域。

using System;public class Program{    static void Main()    {        AppDomain domain = AppDomain.CreateDomain("RemoteDomain");        // 在远端应用程式域中建立RemoteCalculator的实例        RemoteCalculator remoteCalculator = (RemoteCalculator)domain.CreateInstanceAndUnwrap(            typeof(RemoteCalculator).Assembly.FullName, typeof(RemoteCalculator).FullName);        // 使用远端的RemoteCalculator物件进行计算        int result = remoteCalculator.Add(5, 10);        Console.WriteLine("计算结果: " + result);        // 卸载远端应用程式域        AppDomain.Unload(domain);        Console.ReadLine();    }}

在这个範例中,RemoteCalculator类别可以在远端应用程式域中实例化并使用,而不需要将整个物件序列化并传送到另一个应用程式域。这样可以节省资源,同时提供跨应用程式域的物件通讯能力。

远端AppDomain的概念

当你在.NET Framework中建立应用程式时,应用程式执行在称为应用程式域(AppDomain)的隔离环境中。每个应用程式域都有自己的记忆体空间,并提供了一个独立的执行环境,包含运行中的程式码、资源和执行绪。

远端应用程式域是一个应用程式域,但它在不同的机器或处理程序中运行。这意味着你可以在不同的应用程式域之间进行跨越机器或处理程序的通讯,将物件传递给远端应用程式域并操作它们。

MarshalByRefObject是一个基底类别,你可以继承它来建立远端物件。当你的类别继承自MarshalByRefObject,它的实例可以被视为远端物件,并可以在不同的应用程式域之间传递。

在上述的範例中,我们使用AppDomain.CreateDomain方法来建立一个新的应用程式域,并指定它的名称为"RemoteDomain"。然后,我们使用domain.CreateInstanceAndUnwrap方法在远端应用程式域中建立RemoteCalculator类别的实例。

这样做的好处是,你可以在主应用程式域中建立一个远端物件,然后将其传递给其他应用程式域,使它们能够使用该物件的功能,而不需要将整个物件序列化和传送。这种方式可以节省资源,并提供了一个方便的机制来实现分散式应用程式中的物件通讯。

取得原本的AppDomain物件,该如何做?

在上述範例中,要取得原本的应用程式域(AppDomain)物件,你可以使用AppDomain.CurrentDomain属性。这个属性会回传目前执行程式码的应用程式域物件。

以下是修改后的範例程式码,展示如何取得原始应用程式域物件:

using System;public class Program{    static void Main()    {        // 取得原始应用程式域物件        AppDomain originalDomain = AppDomain.CurrentDomain;        AppDomain remoteDomain = AppDomain.CreateDomain("RemoteDomain");        // 在远端应用程式域中建立RemoteCalculator的实例        RemoteCalculator remoteCalculator = (RemoteCalculator)remoteDomain.CreateInstanceAndUnwrap(            typeof(RemoteCalculator).Assembly.FullName, typeof(RemoteCalculator).FullName);        // 使用远端的RemoteCalculator物件进行计算        int result = remoteCalculator.Add(5, 10);        Console.WriteLine("计算结果: " + result);        // 卸载远端应用程式域        AppDomain.Unload(remoteDomain);        // 回到原始应用程式域        Console.WriteLine("回到原始应用程式域");        result = remoteCalculator.Add(3, 7);        Console.WriteLine("计算结果: " + result);        Console.ReadLine();    }}

在这个修改后的範例中,我们在Main方法一开始使用AppDomain.CurrentDomain来取得原始应用程式域的物件,并将它存储在originalDomain变数中。接着,在远端应用程式域中进行运算后,我们再一次使用remoteCalculator物件进行计算,此时会回到原始应用程式域。

这样你就可以在远端应用程式域中进行操作,然后回到原始应用程式域并使用原始的应用程式域物件。

取得CurrentDomain通常会用来做什么?

AppDomain.CurrentDomain通常用于取得目前执行程式码的应用程式域(AppDomain)物件。这个物件可以让你存取和操作目前应用程式域的相关资讯和功能。

以下是一些常见的使用情境:

卸载应用程式域:你可以使用AppDomain.CurrentDomain物件的Unload方法,将目前的应用程式域卸载,并释放相关的资源。这在需要释放执行中的应用程式域时非常有用,例如在插件式架构中卸载插件或在某些场景中重新载入程式码。

注册应用程式域事件:AppDomain.CurrentDomain物件提供了许多事件,你可以注册这些事件来监听应用程式域的生命週期事件,例如执行绪的建立和终止、组件的载入和卸载等。这样可以让你在应用程式域生命週期中採取相应的操作或记录相关资讯。

存取应用程式域的属性和方法:AppDomain.CurrentDomain物件提供了许多属性和方法,让你存取和操作目前应用程式域的属性和相关资讯。例如,你可以使用SetupInformation属性来取得应用程式域的配置讯息,使用BaseDirectory属性来取得应用程式域的基础目录,或使用Load方法来载入组件到目前的应用程式域。

总之,AppDomain.CurrentDomain物件提供了一个方便的方式来存取和操作目前应用程式域的相关资讯和功能。使用它,你可以进行应用程式域的管理、事件监听和存取域内的属性和方法。

如果在Console.ReadLine之前先Unload CurrentDomain,会出现错误吗?

如果在Console.ReadLine之前尝试卸载目前的应用程式域 (AppDomain.CurrentDomain.Unload),通常会引发CannotUnloadAppDomainException例外。这是因为目前的应用程式域正在执行中,且尚未完成其生命週期。

卸载应用程式域是一个较为重大的操作,需要确保应用程式域内的所有工作都已完成并正确释放相关资源。在这种情况下,Console.ReadLine之后程式才会结束,因此在该行程式码执行之前尝试卸载应用程式域会导致异常。

若想卸载应用程式域,建议将卸载程式码放在程式的适当位置,以确保在结束前已完成所有必要的操作。例如,可以将卸载程式码放在Console.ReadLine之后,或是在某个特定的程式逻辑完成后再进行卸载。

当然,具体是否会出现错误还取决于应用程式域内正在执行的工作以及相关的资源。某些情况下,即使尝试卸载目前的应用程式域,也可能不会立即引发例外,但这样的做法仍然是不建议的,因为可能会导致意外的行为或潜在的资源洩漏。

因此,建议在应用程式域的生命週期结束后,再进行卸载操作,以确保程式的正确执行。

何时会需要用到CreateDomain?

CreateDomain方法通常在以下情况下会用到:

插件式架构:当你想要在应用程式中载入并执行外部组件或模组时,可以使用CreateDomain方法在新的应用程式域中建立一个隔离的环境。这样可以确保外部组件的执行不会对主应用程式造成不良影响。在需要时,你可以卸载该应用程式域,并释放相关的资源。

程式码隔离和安全性:当你希望执行不受信任的程式码时,可以使用CreateDomain方法在新的应用程式域中执行该程式码。这样可以提供更高的隔离性和安全性,以防止不受信任的程式码对系统造成损害。在需要时,你可以卸载该应用程式域,从而停止不受信任的程式码的执行并清理相关的资源。

动态载入和卸载组件:当你需要在运行时动态载入和卸载组件时,可以使用CreateDomain方法在新的应用程式域中载入组件。这样可以提供更大的灵活性和可扩展性,使你能够动态地扩充应用程式的功能。在需要时,你可以卸载该应用程式域,从而卸载已载入的组件并清理相关的资源。

请注意,使用CreateDomain方法建立新的应用程式域需要一些额外的考虑和管理。应用程式域之间的通讯、资源共享和安全性等都需要谨慎处理。在使用CreateDomain方法时,建议仔细考虑你的需求和目标,并确保适当地设计和管理相关的应用程式域。

CreateDomain举例

当你使用外部插件或模组时,可以使用CreateDomain方法在新的应用程式域中执行该插件的程式码。这样可以隔离插件的执行环境,以防止它对主应用程式造成损害,同时也提供了更大的灵活性和可扩展性。

以下是一个简单的例子:

假设你正在开发一个图像处理应用程式,并希望支援外部插件,让使用者可以撰写自己的图像滤镜。

首先,你可以定义一个介面 IFilter,代表图像滤镜的功能:

public interface IFilter{    Bitmap ApplyFilter(Bitmap image);}

然后,你可以建立一个新的应用程式域,并在该域中载入使用者提供的插件组件:

AppDomain pluginDomain = AppDomain.CreateDomain("PluginDomain");string pluginAssemblyPath = "path/to/plugin.dll";string pluginTypeName = "PluginNamespace.PluginClass";IFilter pluginFilter = (IFilter)pluginDomain.CreateInstanceAndUnwrap(pluginAssemblyPath, pluginTypeName);

在这个範例中,假设插件组件位于 pluginAssemblyPath,插件的类别名称为 PluginNamespace.PluginClass,并且该类别实作了 IFilter 介面。

现在,你可以使用 pluginFilter 物件来套用图像滤镜:

Bitmap originalImage = LoadImage("path/to/image.jpg");Bitmap filteredImage = pluginFilter.ApplyFilter(originalImage);SaveImage(filteredImage, "path/to/filtered_image.jpg");

在上述程式码中,你可以使用 pluginFilter 物件套用插件所提供的图像滤镜到原始图像上,然后将处理后的图像保存到指定路径。

最后,当你不再需要插件时,可以卸载该应用程式域:

AppDomain.Unload(pluginDomain);

这样就可以清理插件所佔用的资源并结束与该插件的交互作用。

透过在新的应用程式域中执行插件的程式码,你可以实现插件的隔离性和弹性,同时保护主应用程式的稳定性和安全性。此外,使用应用程式域还可以让你在运行时动态载入和卸载插件,以扩充应用程式的功能


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章