將Asp.Net Core和corefx移植到.Net 4.0


引言

因為工作內容的原因需要兼容 XP,而 XP 最多支持到.Net Framework 4.0。因此無法享受到 .Net Core 帶來的一堆很好用的庫,好在無論 corefx 還是 Asp.Net Core 都是開源的,我們可以自行修改編譯出支持 .Net 4.0 的版本。

技術可行性

Net 4.0 相比 4.5 和 netstandard 1.0,主要的差別有:

  • System.Threading.Tasks.Task 類型。.Net 4.0 的 Task 沒有 GetAwaiter 成員,編譯器無法生成使用 async await 的代碼。好在編譯器查找 Task.GetAwaiter 並不是直接查找成員方法,如果是擴展方法也可以,那么我們通過對 Task 編寫 GetAwaiter 擴展方法就可以在 .Net 4.0 中使用 async await 了。
  • Reflection 類別的 API。.Net 4.5 對反射 API 進行了重構,分離出了 TypeInfo 用於運行時反射。在 .Net 4.0 中,我們可以自行編寫一個 TypeInfo 類,但是 .Net 4.5 中 TypeInfo 繼承自 Type。據我觀察實現中 GetType 返回的對象實際上就是一個 TypeInfo 對象,這一點我們無法通過自己編寫的 TypeInfo 做到,不過好在除了 mscorlib,其他庫並沒有用到這層繼承關系,因此為了不產生問題我實現的 TypeInfo 沒有繼承自 Type。
  • WeakReference<T>。.Net 4.5 中的 WeakReference<T> 並沒有繼承自 WeakReference,它的終結器方法是一個 InternalCall,也就是在 clr 中實現的。而我們無法修改 clr 的實現,因此我實現的 WeakReference<T> 繼承自 WeakReference,重用了它的終結器。
  • 其它一些類型中 API 的缺失。主要包括 GC、Cryptography。對於沒有的類型,我們可以添加實現,但對於已存在的類型是沒有辦法進行修改的。雖然 clr 中有 TypeForwardTo,配合 assembly redirecting 技術可以替換類型的實現,但 .Net 4.0 編譯的程序集默認引用了 mscorlib,而 mscorlib 並不能被 redirect,所以對於 mscorlib 中已存在類型的 API 缺失——這一點暫時沒有辦法解決。

已經移植的項目

corefx

  • System.Runtime:添加了 ExceptionDispatchInfo、IReadOnlyCollection<T>等一些只讀集合的接口、生成異步方法所需要的 AsyncStateMachineAttribute、以及使用 MVVM 中很常用的 CallerMemberNameAttribute 等
  • System.AppContext:添加了 AppContext 類
  • System.Runtime.CompilerServices.Unsafe:添加了 Unsafe 類(ref 和 指針轉換、直接讀寫內存等)
  • System.Threading:添加了 Volatile 類
  • System.Threading.Tasks:添加支持 async await 相關的類
  • System.Security.Cryptography.Algorithms:添加了 IncrementalHash 的實現

Asp.Net Core

  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.Options
  • Microsoft.Extensions.Configuration

這些寫過 Asp,Net Core 的應該很熟悉,他們也可以用在普通的 .Net 桌面程序中

一些開源項目

  • Autofac:一個功能很強大的 IoC 實現
  • AutoMapper:對象間的映射
  • MaterialDesignThemes:WPF 的 MaterialDesign

示例

  • 新建一個 .Net 4.0 項目
  • 在 Nuget 程序包源里加上 https://www.myget.org/F/dotnet40/api/v3/index.json,並將優先級調到最上面
  • install-package System.Threading.Tasks -Version 4.3.0-net40(注意一定要加上 Version)
  • 在 app.config 加上 assembly redirecting
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Threading" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.12.0" newVersion="4.0.12.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.12.0" newVersion="4.0.12.0" />
      </dependentAssembly>
  •  然后你就可以愉快的 async await 了

 

下面的示例是使用了

Caliburn.Micro

Microsoft.Extensions.DependencyInjection

Microsoft.Extensions.Configuration

Autofac

Autofac.Extensions.DependencyInjection

AutoMapper

AutoMapper.Extensions.Microsoft.DependencyInjection

    public class AppBootstrapper : BootstrapperBase
    {
        public IConfiguration Configuration { get; }
        public IServiceProvider ServiceProvider { get; private set; }
        private IContainer _container;

        public AppBootstrapper()
        {
            Configuration = LoadConfiguration();
            Initialize();
        }

        private IConfiguration LoadConfiguration()
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("config.json", false, false);
            return builder.Build();
        }

        protected override void Configure()
        {
            var serviceCollection = new ServiceCollection();
            ServiceProvider = ConfigureServices(serviceCollection);
        }

        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();
            services.AddAutoMapper(AssemblySource.Instance.ToArray());
            services.AddSingleton<IWindowManager>(new WindowManager());
            services.AddSingleton<IEventAggregator>(new EventAggregator());
            
            services.AddSingleton(p => _container);

            var builder = new ContainerBuilder();
            builder.Populate(services);
            builder.RegisterAssemblyModules(AssemblySource.Instance.ToArray());

            _container = builder.Build();
            return new AutofacServiceProvider(_container);
        }
}

 看起來和在 Asp.Net Core 中沒什么差別。

總結

雖然工作環境限制我們只能使用 .Net 4.0,但俗話說沒有條件,創造條件也要上。將它們移植到 .Net 4.0 也是跟上 .Net Core 和開源的步伐的一種努力吧。

關於這些包和相關的版本號可以在 https://www.myget.org/feed/Packages/dotnet40 查看

關於移植到 .Net 4.0 的計划我創建了一個 github 組織,里面包含移植的所有項目 https://github.com/dotnet40/

最后,感謝大家花時間閱讀!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM