dotnet C# 只創建對象不調用構造函數方法

有時我期望只是創建出對象,但是不要調用對象的構造方法,可以通過使用 FormatterServices 的 GetUninitializedObject 函數來實現只創建對象不調用構造函數方法

這個 FormatterServices.GetUninitializedObject 方法大部分是用在做序列化使用的,然而在很多 IOC 容器,也都使用此方法來創建對象,而通過其他方法拿到構造函數

在 WPF 的 XAML 創建對象,也有用到此方法,詳細請看 dotnet 讀 WPF 源代碼筆記 XAML 創建對象的方法

以下是一個實現的例子

            Foo foo = null;
            try
            {
                foo = (Foo) FormatterServices.GetUninitializedObject(typeof(Foo));
                var constructorInfo = typeof(Foo).GetConstructor(new Type[0]);
                constructorInfo!.Invoke(foo, null);
            }
            catch
            {
            }

class Foo
{

}

此方法可以用來處理在構造函數時,如果拋出了異常,但是此對象的 Dispose 需要被顯式調用的問題。因爲如果在構造函數拋出異常,那麼在 C# 代碼層面將拿不到此對象,也就無法調用對應的 Dispose 釋放

如以下代碼,可以看到 Foo 對象依然是空

        private void F1()
        {
            Foo foo = null;
            try
            {
                foo = new Foo();
            }
            catch
            {
                // 忽略
            }
        }

    class Foo : IDisposable
    {
        public Foo()
        {
            throw new Exception("lindexi is doubi");
        }

        ~Foo()
        {
        }

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

此時如果期望調用 Foo 對象的 Dispose 方法,將會因爲拿不到對象而無法調用

解決此方法的做法就是通過只創建對象而不調用構造的方法,先拿到對象然後再調用構造,如果構造出錯,依然還可以調用對象的 Dispose 方法

        private void F2()
        {
            Foo foo = null;
            try
            {
                foo = (Foo) FormatterServices.GetUninitializedObject(typeof(Foo));
                var constructorInfo = typeof(Foo).GetConstructor(new Type[0]);
                constructorInfo!.Invoke(foo, null);
            }
            catch
            {
                // 忽略
            }
            finally
            {
                try
                {
                    foo?.Dispose();
                }
                catch
                {
                    // 可以調用到 Dispose 方法
                }
            }
        }

    class Foo : IDisposable
    {
        public Foo()
        {
            throw new Exception("lindexi is doubi");
        }

        ~Foo()
        {
            Dispose();
        }

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

            throw new Exception($"lsj is doubi");
        }
    }

這個設計可以用來解決,如果對象的構造函數還沒完全完成,調用釋放函數將會拋出異常。如果沒有使用如上方法,那麼在釋放函數的異常將會在 GC 回收線程拋出,而讓應用程序退出

這就是爲什麼有很多容器和底層庫喜歡使用此方法創建對象的原因

本文代碼還請到 githubgitee 上閱讀代碼

可以通過如下方式獲取本文的源代碼,先創建一個空文件夾,接着使用命令行 cd 命令進入此空文件夾,在命令行裏面輸入以下代碼,即可獲取到本文的代碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 11077dd21a4ee5314757536ca379ecca6956b040

以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換爲 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

獲取代碼之後,進入 HojeneceabuHallwhallhebo 文件夾

FormatterServices.GetUninitializedObject(Type) Method (System.Runtime.Serialization)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章