引言 因為工作內容的原因需要相容 XP,而 XP 最多支持到.Net Framework 4.0。因此無法享受到 .Net Core 帶來的一堆很好用的庫,好在無論 corefx 還是 Asp.Net Core 都是開源的,我們可以自行修改編譯出支持 .Net 4.0 的版本。 技術可行性 Net ...
引言
因為工作內容的原因需要相容 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/
最後,感謝大家花時間閱讀!