.NET Core/.NET 5.0 析构函数依然有效?

前言

最近看到小伙伴在.NET Core中用到了析构函数,不由打一疑问,大部分状况下,即便在.NET Framework中都不会怎么用到析构函数,我想在.NET Core中是否还依然有效呢?随着时间推移,迭代版本更新,有些当初咱们脑海里认定的东西可能在当前并再也不适用,这也就须要咱们同步知识更新,现在咱们所认为可能并再也不是往昔咱们所认为git

.NET Core/.NET 5.0 析构函数

下面首先来看在.NET Framework中一个很标准的资源释放例子,这里我以4.7.2版本为例(其余版本同样)。建立基于当前应用程序域的指定程序集的指定实例github

public class CurrentDomainSandbox : IDisposable
{
    private AppDomain _domain = AppDomain.CreateDomain(
      "CurrentDomainSandbox",
      null,
      new AppDomainSetup
      {
        ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
        ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
      });

    ~CurrentDomainSandbox()
    {
      Dispose(false);
    }

    public T CreateInstance<T>(params object[] args)
      => (T)CreateInstance(typeof(T), args);

    private object CreateInstance(Type type, params object[] args)
    {
      HandleDisposed();

      return _domain.CreateInstanceAndUnwrap(
        type.Assembly.FullName,
        type.FullName,
        ignoreCase: false,
        bindingAttr: 0,
        binder: null,
        args: args,
        culture: null,
        activationAttributes: null);
    }

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (disposing && (_domain != null))
      {
        AppDomain.Unload(_domain);
        _domain = null;
      }
    }

    private void HandleDisposed()
    {
      if (_domain == null)
      {
        throw new ObjectDisposedException(null);
      }
    }
}

经过如上定义建立指定名称的应用程序域沙箱盒子,这样咱们则可在此沙箱中建立对应程序集和实例,如此则能够其余域彻底隔离且独立,而后在控制台进行以下调用dom

  var sanBox = new CurrentDomainSandbox();
  var instance = sanBox.CreateInstance<Program>();

还未完毕,直接运行将抛出以下异常ide

 若用于远程传输,咱们直接将主类继承自MarshalByRefObject就好,不然将此类经过Serializable特性标记,至于两者区别不详细展开。经过上述比较标准的例子咱们则能够建立和释放未被使用的对应实例,咱们看到用到了析构函数,可是咱们发现最终调用Dispose方法,并未作任何处理,其实否则,问题出在对析构函数概念的理解函数

 

析构函数:在应用程序终止以前,将调用还没有被垃圾回收的全部对象的析构函数。析构函数本质是终结器,若是对象已被释放,在合适时机将自动调用Finalize方法,除非咱们手动经过GC来抑制调用终结器(GC.SuppressFinalize),但不建议手动调用Finalize方法性能

 

经过资源释放标准例子,想必咱们已经知道了析构函数的基本原理,接下来咱们仍是基于上述.NET Framework 4.7.2版原本演示析构函数ui

public class ExampleDestructor
{
    public ExampleDestructor()
    {
      Console.WriteLine("初始化对象");
    }

    public void InvokeExampleMethod()
    {

    }

    ~ExampleDestructor()
    {
      Console.WriteLine("终结对象");
    }
}

既然析构函数是在应用程序终止前进行调用,那么咱们在调用上述示例中方法时,以下调用:this

var exampleDestructor = new ExampleDestructor();

exampleDestructor.InvokeExampleMethod();

在.NET Framework中如咱们所指望,在应用程序卸载时,此时会调用析构函数并进行相关打印。接下来到.NET Core,此时将断点放在析构函数中,将不会再调用,打印以下:url

好了,以上只是我我的猜想,接下来咱们直接看官方文档进行论证,官网对于析构函数连接spa

析构函数规范

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/destructors

在.NET Framework应用程序中会尽一切合理努力在程序退出时调用析构函数进行清理(调用终结器方法),除非进行手动抑制,但在.NET Core并不能彻底保证此行为。经过调用Collect来强制进行垃圾回收,可是在大多数状况下,应避免此调用,由于这可能会致使性能问题。为什么出现如此差别呢?更详细分析请参看连接:

.NET Core析构函数理解分析

https://github.com/dotnet/runtime/issues/16028

根据此连接表述,能够这样理解:在.NET Core中不会在应用程序终止时运行终结器(针对可到达或不可到达的对象),根据建议,并不能保证全部可终结对象在关闭以前都将被终结。因为上述连接缘由存在,因此在ECMA的C#5.0规范削弱了这一要求,所以.Net Core并不会违反此版本规范

总结

💡 在应用程序关闭前,.NET Framework会尽一切合理努力调用析构函数即终结器进行资源清理,但在.NET Core中并不能保证此行为,因此在ECMA 语言规范中削弱了这一要求

💡 基于上述,在.NET Core中使用析构函数并无实质性意义

相关文章
相关标签/搜索