背景
多くの場合、ツールライブラリを開発した後、特定のフレームワークバージョンで問題が発生することがあります。例えば、最近私が開発したインポート/エクスポートツールライブラリMagicodes.IE(GitHub:https://github.com/xin-lai/Magicodes.IE)では以下の問題が発生しました:
標準ライブラリ2.0をサポートしているにもかかわらず、ユニットテストの実行により以下の問題が明らかになりました:
したがって、ユニットテストは各フレームワークバージョンに対して実行する必要があり、関連するコードはフレームワークに合わせて互換性の修正が必要です。では、どのように実装するのでしょうか?以下で段階的に実践していきます。
.NETフレームワークバージョンの説明
最新のターゲットフレームワークバージョン
下表は、最も一般的なターゲットフレームワーク、それらの参照方法、および実装されている.NET Standardバージョンを定義しています。これらのターゲットフレームワークバージョンは最新の安定版です。プレビュー版は表示されません。ターゲットフレームワーク名オブジェクト(TFM)は、.NETアプリまたはライブラリのターゲットフレームワークを指定するための標準化されたトークン形式です。
| ターゲットフレームワーク | 最新安定版 | ターゲットフレームワーク名オブジェクト(TFM) | 実装.NET Standardバージョン |
|---|---|---|---|
| .NET Standard | 2.1 | netstandard2.1 | 不適用 |
| .NET Core | 3.0 | netcoreapp3.0 | 2.1 |
| .NET Framework | 4.8 | net48 | 2.0 |
サポートされるターゲットフレームワークバージョン
ターゲットフレームワークは通常TFMで参照されます。下表は.NET Core SDKおよびNuGetクライアントがサポートするターゲットフレームワークを示しています。同等のものは括弧内に表示されます。例えば、win81はnetcore451に対するTFMとして同等です。
| ターゲットフレームワーク | TFM |
|---|---|
| .NET Standard | netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netstandard2.1 |
| .NET Core | netcoreapp1.0 netcoreapp1.1 netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 |
| .NET Framework | net11 net20 net35 net40 net403 net45 net451 net452 net46 net461 net462 net47 net471 net472 net48 |
| Windows ストアアプリ | netcore [netcore45] netcore45 [win] [win8] netcore451 [win81] |
| .NET Micro Framework | netmf |
| Silverlight | sl4 sl5 |
| Windows Phone | wp [wp7] wp7 wp75 wp8 wp81 wpa81 |
| ユニバーサルWindowsプラットフォーム | uap [uap10.0] uap10.0 [win10] [netcore50] |
プロジェクトが複数のフレームワークをサポートする方法
ユニットテストプロジェクトを例に取ります:
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;netcoreapp2.2;netcoreapp3.1;net461</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
上記のコードのように、「TargetFrameworks」要素を使用して複数のフレームワークを定義できます。では、プロジェクト内でどのように条件判断を追加してコンパイルするのでしょうか?
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
プロジェクト内だけでなく、コード内でどのように異なるフレームワークバージョンに対応したコードを記述するのでしょうか?
#if NET461
return excelPackage.Workbook.Worksheets[typeof(T).GetDisplayName()] ??
excelPackage.Workbook.Worksheets[ExcelImporterSettings.SheetName] ??
excelPackage.Workbook.Worksheets[1];
#else
return excelPackage.Workbook.Worksheets[typeof(T).GetDisplayName()] ??
excelPackage.Workbook.Worksheets[ExcelImporterSettings.SheetName] ??
excelPackage.Workbook.Worksheets[0];
#endif
これらの「NET461」のようなシンボルにはどのようなものがあるのでしょうか?以下に示します:
完全な.NET Coreターゲットフレームワークのプリプロセッサシンボルリスト
| ターゲットフレームワーク | シンボル |
|---|---|
| .NET Framework | NETFRAMEWORK, NET20, NET35, NET40, NET45, NET451, NET452, NET46, NET461, NET462, NET47, NET471, NET472, NET48 |
| .NET Standard | NETSTANDARD, NETSTANDARD1\_0, NETSTANDARD1\_1, NETSTANDARD1\_2, NETSTANDARD1\_3, NETSTANDARD1\_4, NETSTANDARD1\_5, NETSTANDARD1\_6, NETSTANDARD2\_0, NETSTANDARD2\_1 |
| .NET Core | NETCOREAPP, NETCOREAPP1\_0, NETCOREAPP1\_1, NETCOREAPP2\_0, NETCOREAPP2\_1, NETCOREAPP2\_2, NETCOREAPP3\_0, NETCOREAPP3\_1 |
Magicodes.IEのマルチフレームワークバージョンテスト
これらの知識を理解すれば、マルチフレームワークバージョンの実装とユニットテストを記述できます。
ユニットテストにマルチフレームワークサポートを追加
-
TargetFrameworkをTargetFrameworksに変更する
-
グループ設定
ターゲットフレームワークに基づいてグループ化します:
- コンパイルエラーの修正
- ユニットテストの実行
ユニットテストの結果は以下の図のようになります:
具体的なユニットテストについては、ターゲットフレームワークを指定して実行およびデバッグすることもできます:
経験とベストプラクティスの共有
改善プロセスを総合すると、以下の経験を得られます:
- ユニットテストの例外メッセージ判断では、完全な形式を判断するのではなく、文字列包含判断を使用することを推奨します。
上記のように、net461、netcoreapp2.2では左側のコードは通過できません。フォーマットされたパラメータテンプレートが一貫していません。
- 特定のフレームワーク、プラットフォームのコードにはシンボル判断を追加する
以下の例のように、デフォルトでは.NET Coreはコードページ28591以外の他のコードページエンコーディングやUnicodeエンコーディング(UTF-8やUTF-16など)を提供しないため、以下のコードを使用して追加できますが、.NET Frameworkを除外する必要があります:
特定のコード以外にも、特定の依存パッケージが必要な場合があります:
- 新しい構文糖を慎重に使用する
以下のコードのような場合:
リソースに限りがあり、あまり多くのフレームワークバージョンに対応できませんが、興味のある方は参加してください。