C#による.NET Core入門(5)
.NET Standardなライブラリプロジェクトを作成して参照する
クロスプラットフォームで開発できる.NET Coreの基礎から開発実践までが学べる入門連載。5回目は.NET Standardなクラスライブラリなプロジェクトを作成し、別のコンソールプロジェクトから参照する方法を説明する。
クラス・ライブラリ・プロジェクトの作成
プロジェクトとソリューションの作成
今回はクラスライブラリプロジェクトを作成し、そのプロジェクトを参照するコンソールプロジェクトを作成する。また2つのプロジェクトをまとめるソリューションも作成する。まずリスト1の一連のコマンドを実行し、ソリューションの作成、プロジェクトの作成、プロジェクトのソリューションへの追加を行ってほしい。
$ mkdir Example4
$ cd Example4
$ dotnet new sln
$ dotnet new console -o Example4.ConsoleApp
$ dotnet sln add Example4.ConsoleApp/Example4.ConsoleApp.csproj
$ dotnet new classlib -o Example4.Lib
$ dotnet sln add Example4.Lib/Example4.Lib.csproj
|
なお、これ以降のシェル上でのコマンド実行は、リスト1実行後のカレントディレクトリである、ソリューションファイルが配置されているディレクトリを基準に全て実行してある。カレントディレクトリの移動が必要な場合は、最初にcd
コマンドで移動している。
dotnet new classlib
で指定しているclasslib
というのがデフォルトで用意されているクラスライブラリプロジェクトのテンプレートだ。作られたクラスライブラリプロジェクトにはClass1.cs
と<プロジェクト名>.csproj
の2つのファイルが存在しているはずである。Class1.cs
は適宜リネームや削除して必要なクラスファイルを追加すればよいが、まず<プロジェクト名>.csproj
ファイルの中身を見てみよう。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
|
XMLの<Project>
要素は、前回説明したコンソールプロジェクトと同じである。前回のリスト2と見比べると、<PropertyGroup>
の子要素の<OutputType>
要素がなくなり、<TargetFramework>
要素がnetstandard2.0
となっているところが違う要素である。
もしこのクラスライブラリプロジェクトを.NET Core 2.0以降のみから参照できるライブラリとして開発するのであれば、この<TargetFramework>
要素をnetcoreapp2.0に変更することもできる。しかし、netstandard2.0とすることにより、既存の.NET Frameworkをはじめ、Xamarinの各プラットフォームやUWPといったプロジェクトからも参照できるクラスライブラリになり汎用性が増す。特に.NET Core 2.0向けのライブラリとしてまず開発する場合、ほとんどのケースでnetstandard2.0として問題がないため、クラスライブラリプロジェクトの<TargetFramework>
はnetstandard2.0を指定して開発を進めていくことを強くお勧めする。
また、可能であればより低い.NET Standardのバージョンを指定することをお勧めする。この理由については、次の「.NET Standard 2.0とは」の項で説明する。
.NET Standard 2.0とは
もともと.NETは、Windows上の.NET Frameworkという単一実装のみで動いていた。そのため、あるバージョンの.NET FrameworkのクラスライブラリのAPI群は、そのまま同じバージョンの.NET Frameworkの実装で動くことが確実だった。
その後、SilverlightやWindows Phoneアプリなど、複数の.NETプラットフォームが出てきて、対象プラットフォーム(Windows上のフル.NET Frameworkなのか、Silverlightなのか、Windows Phoneなのか)によってサポートするAPIに違いが生じてきた。確かに違いはあるものの、共通部分も多くあるため、「プラットフォームの差異による影響を受けない特殊なクラスライブラリを利用することで、共通部分は同じソースコードで動かしたい」という要望から、プロファイルベースのポータブル・クラス・ライブリ(PCL)という仕組みが生まれた。
ただしPCLにも課題が生じてきている。具体的には、PCLはお互いのプラットフォームがサポートするAPIの共通部分という形を採っていたため、組み合わせが増えるとより複雑なサポートAPIの管理が必要になってきたのだ。
さらに.NET Coreが出てきた現在、.NETが正式にサポートするプラットフォームは、
- .NET Core
- .NET Framework
- mono
- Xamarin (iOS/macOS/Android)
- UWP
など広範囲に及ぶようになった。そのため、プロファイルベースの管理ではなく、.NET Standardという仕組みを新たに導入するに至った。
.NET Standardは、「それぞれの.NET実装が実装すべきAPI群をあらかじめ決めておき、各プラットフォームでの動作をサポートする.NET実装が実際にそのAPIを実装してサポートする」という仕組みになっている。ターゲットとする.NET Standardのバージョンを決めておくことで、クラスライブラリを開発するときは、サポートされるAPIのみを参照して開発できる。また、完成したクラスライブラリは、そのバージョンの.NET Standardに対応している全ての.NET実装で参照できる。
.NET Standardのバージョンごとにサポートする.NET実装は、公式ドキュメントの表を参照してほしい(参考:表1に執筆時点のものを引用転載している)。
.NET Standard | 1 | 1.1 | 1.2 | 1.3 | 1.4 | 1.5 | 1.6 | 2 |
---|---|---|---|---|---|---|---|---|
.NET Core | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 |
.NET Core 1.x SDKを使用しての.NET Framework | 4.5 | 4.5 | 4.5.1 | 4.6 | 4.6.1 | 4.6.2 | ||
.NET Core 2.0 SDKを使用しての.NET Framework | 4.5 | 4.5 | 4.5.1 | 4.6 | 4.6.1 | 4.6.1 | 4.6.1 | 4.6.1 |
Mono | 4.6 | 4.6 | 4.6 | 4.6 | 4.6 | 4.6 | 4.6 | 5.4 |
Xamarin.iOS | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10.14 |
Xamarin.Mac | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3.8 |
Xamarin.Android | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 8 |
UWP | 10 | 10 | 10 | 10 | 10 | vNext | vNext | vNext |
Windows | 8 | 8 | 8.1 | |||||
Windows Phone | 8.1 | 8.1 | 8.1 | |||||
Windows Phone Silverlight | 8 |
まず、.NET実装の各バージョンから、対応する.NET Standardのバージョンを確認してみよう。
.NET Core 2.0に関して、表1の黄色の背景色で示した部分を見てほしい。.NET Core 2.0(netcoreapp2.0)をターゲットとするプロジェクトは、.NET Standard 2.0(netstandard2.0)をターゲットとするクラスライブラリを参照できることが分かる。
また.NET Core 1.0に関して、黄色の背景色の左側にある列を見ほしい。.NET Core 1.0(netcoreapp1.0)をターゲットとするプロジェクトは、.NET Standard 1.6およびそれより小さいバージョン(1.5/1.4/1.3/1.2/1.1/1)をターゲットとするクラスライブラリを参照できることが分かる。
次に、.NET Standardのバージョンから、それを実装している各.NET実装のバージョンを確認してみよう(先ほどとは逆の視点になる)。
表1の緑色の背景色で示した部分を見てほしい。もし.NET Standard 2.0を指定すると、サポートされる最小のバージョンは、.NET Frameworkだと4.6.1と比較的新しいバージョンにしないといけない。他の.NET実装も同様に最新バージョンが必要になる。
もちろん、.NET Standard 2.0の方が1.6やそれ以前より利用可能なAPIが増えているので、.NET Standard 2.0でのみ利用できるAPIを利用する必要がある場合は.NET Standard 2.0と指定するしかないだろう。しかし、より小さなバージョンの.NET Standardで開発可能な場合は、小さいバージョンを指定しておいた方が、そのクラスライブラリを利用できるプラットフォームが多くなるわけである。
例えば、.NET Standard 2.0の1つ前の.NET Standard 1.6やそれより小さいバージョン(1.5/1.4/1.3/1.2/1.1/1)を指定してクラスライブラリを開発した場合、.NET Core 1.0以降のバージョンをターゲットとしているプロジェクトで利用できる、つまり.NET Core 2.0をターゲットとしているプロジェクトからも参照できるので、.NET Standard 2.0を指定するよりも幅広いバージョンとプラットフォームで利用できるようになる。これが「可能であればより低い.NET Standardのバージョンを指定することをお勧めする」理由だ。
また、クラスライブラリで複数の.NET Standardのバージョンをターゲットとすることもできる。.NET Standard 2.0でのみ利用可能なAPIを利用する部分をプリプロセッサディレクティブで切り分けることで、同じソースコードファイルから複数のバージョン向けの.NET Standard向けのバイナリを出力できる。この方法については最後の方の節で紹介する。
クラスライブラリプロジェクトの作成
それでは実際にクラスライブラリプロジェクトを作成してみよう。
クラスライブラリプロジェクトは、コンソールプロジェクトと違い、エントリポイントとなるMain
メソッドを持たない。他のプロジェクトがクラスライブラリを参照して、そのライブラリ内のクラスを利用できるようにするために、public
なクラスとメソッドを定義する。今回は、Class1.cs
ファイルをいったん削除し、MyStringLib.cs
というファイルを作成し、リスト3のようにコードを定義する。
using System;
namespace Example4.Lib
{
public class MyStringLib
{
public static string Copy(string str)
{
return string.Copy(str);
}
}
}
|
編集後、リスト4の1つ目のdotnet
コマンドでクラスライブラリプロジェクトをビルドすると、bin/Debug/netcoreapp2.0
フォルダーにExample4.ConsoleApp.dll
というバイナリファイルが生成される。
配置用に発行したい場合は、2つ目のdotnet
コマンドを実行するとbin/Debug/netcoreapp2.0/publish
フォルダー以下に生成されることが確認できる。
$ cd Example4.ConsoleApp
$ dotnet build
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Example4.ConsoleApp -> /home/tatanaka/Example4/Example4.ConsoleApp/bin/Debug/netcoreapp2.0/Example4.ConsoleApp.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:09.40
$ dotnet publish
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Example4.ConsoleApp -> /home/tatanaka/Example4/Example4.ConsoleApp/bin/Debug/netcoreapp2.0/Example4.ConsoleApp.dll
Example4.ConsoleApp -> /home/tatanaka/Example4/Example4.ConsoleApp/bin/Debug/netcoreapp2.0/publish/
|
プロジェクトの参照
コンソールプロジェクトからの参照
次に、このクラスライブラリプロジェクトをコンソールプロジェクトから参照してみよう。リスト5のコマンドを実行すると、コンソールプロジェクトからクラスライブラリプロジェクトへの参照が追加される。
$ cd ..
$ dotnet add Example4.ConsoleApp/Example4.ConsoleApp.csproj reference Example4.Lib/Example4.Lib.csproj
Reference `..\Example4.Lib\Example4.Lib.csproj` added to the project.
|
このコマンドは、リスト6のような<ItemGroup>
要素を、.csproj
ファイルに追加している。実際にリスト5のコマンドを実行した後に.csproj
ファイルを見ると確認できる。
また、.csproj
ファイルを直接編集してこの要素を追加することでも、参照を追加できる。
コンソールプロジェクト側のソースコードからクラスライブラリ側のメソッドを呼び出すコードを、リスト7のように追加した。
using System;
using Example4.Lib;
namespace Example4.ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var testString = "test";
var copiedString = MyStringLib.Copy(testString);
Console.WriteLine($"copied string is {copiedString}");
}
}
}
|
リスト8のようにコマンドを実行すると、コンソールプロジェクトを実行できる。
コマンド出力を見ると分かるように、クラスライブラリプロジェクトをビルドした後、コンソールプロジェクトをビルドしている。あるプロジェクトをビルドするためには、プロジェクト参照している全てのプロジェクトを先にビルドしないといけないため、プロジェクトを相互参照することはできない。
$ cd Example4.ConsoleApp
$ dotnet build
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Example4.Lib -> /home/tatanaka/Example4/Example4.Lib/bin/Debug/netstandard2.0/Example4.Lib.dll
Example4.ConsoleApp -> /home/tatanaka/Example4/Example4.ConsoleApp/bin/Debug/netcoreapp2.0/Example4.ConsoleApp.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:09.40
|
またリスト9のようにコンソールプロジェクトを発行すると、参照しているクラスライブラリプロジェクトの.dllバイナリファイルがbin/Debug/netcoreapp2.0/publish
フォルダーの下に一緒に生成されていることも分かる。これは参照しているライブラリを全て一緒の場所に配置するためである。
$ dotnet publish
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Example4.Lib -> /home/tatanaka/Example4/Example4.Lib/bin/Debug/netstandard2.0/Example4.Lib.dll
Example4.ConsoleApp -> /home/tatanaka/Example4/Example4.ConsoleApp/bin/Debug/netcoreapp2.0/Example4.ConsoleApp.dll
Example4.ConsoleApp -> /home/tatanaka/Example4/Example4.ConsoleApp/bin/Debug/netcoreapp2.0/publish/
|
複数フレームワークをターゲットとするクラスライブラリプロジェクト
例えば、.NET Core 2.0と.NET Core 1.0および.NET Framework 4.5から参照される予定であるクラスライブラリを作る場合、.NET Standard 1.0~1.6の間のいずれかを単一のターゲットとするクラスライブラリを作成すればよい。しかし、先に.NET Standard 2.0で作成している場合や、将来的に.NET Standard 2.0のみの対応を予定している場合など、「.NET Standard 2.0を主なターゲットとしつつ、より小さいバージョンの.NET Standardもターゲットとして追加したい」というケースもあるだろう。
本章では、その実現方法を解説する。その題材として、今作成した.NET Standard 2.0ターゲットのクラスライブラリを、.NET Core 1.0のコンソールプロジェクトから参照しようとするところから始めてみよう(※前述したバージョンの組み合わせを考えると、これは当然エラーになるが、実際にエラーになる点を確認してみる)。
.NET Core 1.0 コンソールプロジェクトの作成
まず.NET Core 1.0のコンソールプロジェクトを作成する。プロジェクトの作成自体は.NET Core SDK 2.0で実行できるが、実行するためには.NET Core SDK 1.0が必要なので、実際に実行したい場合は連載第1回の記事などを参考にインストールしてほしい。.NET Core SDK 1.0と2.0(および1.1や今後リリースが予定されるそれ以外のバージョンも含む)はインストール先のパスを変えておけば共存が可能である。
今回は、~/dotnet-10
以下に.NET Core 1.0 SDKを配置しており、PATH
環境変数には含めず、表示しているコマンドでは~/dotnet-10/dotnet
が.NET Core SDK 1.0の実行コマンドである。
リスト10のコマンドでコンソールプロジェクトを作成した後、Example4.ConsoleApp.NetCoreApp10/Example4.ConsoleApp.NetCoreApp10.csproj
ファイルを直接編集して<TargetFramework>
要素をnetcoreapp1.0に変更してほしい。
$ cd ..
$ ~/dotnet-10/dotnet new console -o Example4.ConsoleApp.NetCoreApp10
|
変更後、.NET Core 2.0のときと同様にリスト11のコマンドでクラスライブラリをプロジェクト参照として追加してみると、エラーが発生した。dotnet
コマンドによるプロジェクト参照の追加は、ターゲットフレームワークに互換性がない場合、エラーが出て追加できないようになっている。また、.csproj
ファイルを直接編集して追加した場合は、ビルド時にエラーが発生する。
$ ~/dotnet-10/dotnet add Example4.ConsoleApp.NetCoreApp10/Example4.ConsoleApp.NetCoreApp10.csproj reference Example4.Lib/Example4.Lib.csproj
Project `/home/tatanaka/Example4/Example4.Lib/Example4.Lib.csproj` cannot be added due to incompatible targeted frameworks between the two projects. Please review the project you are trying to add and verify that is compatible with the following targets:
- netcoreapp1.0
|
ターゲットを複数指定したクラスライブラリプロジェクト
そこで、.NET Core 1.0もサポートしている.NET Standard 1.0をクラスライブラリプロジェクトのターゲットに追加してみよう。
.NET Standard 2.0から「変更」するのではなく「追加」としたのは、.NET Standard 2.0に対応したコードを基本にしつつ、必要な部分だけ.NET Standard 1.0で実行できるコードに切り替えるためである。ターゲットを複数指定する方法は連載第2回でやったように.csproj
ファイルをリスト12のように編集して、<TargetFramework>
要素から<TargetFrameworks>
要素に変更すればよい。
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard1.0</TargetFrameworks>
</PropertyGroup>
|
さて、netstandard2.0とnetstandard1.0を指定したので、.NET Core SDK 2.0のdotnet
コマンドを使って、このコードをビルドしてみよう。リスト13のようにエラーが発生するはずだ。
$ dotnet build
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Example4.Lib -> /home/tatanaka/Example4/Example4.Lib/bin/Debug/netstandard2.0/Example4.Lib.dll
MyStringLib.cs(10,27): error CS0117: 'string' does not contain a definition for 'Copy' [/home/tatanaka/Example4/Example4.Lib/Example4.Lib.csproj]
Build FAILED.
MyStringLib.cs(10,27): error CS0117: 'string' does not contain a definition for 'Copy' [/home/tatanaka/Example4/Example4.Lib/Example4.Lib.csproj]
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:05.61
|
フレームワークを指定しない場合、指定してある全てのターゲットフレームワークに対してビルドを行うが、まず.NET Standard 2.0向けのビルドは成功している。次に.NET Standard 1.0向けのビルドで失敗しているが、これは、string.Copy
メソッドが.NET Standard 2.0で追加されたAPIであるからだ。
そこでこの部分だけifディレクティブ
を使って、.NET Standard 1.0をターゲットフレームワークとしてコンパイルするときは(より正確には.NET Standard 2.0以外をターゲットフレームワークとしてビルドしたときは)同等の処理を行う別のコードに切り替えるようにしてみよう(リスト14)。各ターゲットにおけるコンパイラーに渡されるプリプロセッサシンボルの値は.NET Coreのドキュメントに記載がある。
#if NETSTANDARD2_0
return string.Copy(str);
#else
return new string(str.ToCharArray());
#endif
|
再度ビルドすると、今度は両方のターゲットフレームワークでビルドが成功するはずだ。
$ dotnet build
Microsoft (R) Build Engine version 15.3.409.57025 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Example4.Lib -> /home/tatanaka/Example4/Example4.Lib/bin/Debug/netstandard1.0/Example4.Lib.dll
Example4.Lib -> /home/tatanaka/Example4/Example4.Lib/bin/Debug/netstandard2.0/Example4.Lib.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:05.14
|
そこで、再度リスト11のコマンドを実行すると、.NET Core 1.0のコンソールプロジェクトにこのクラスライブラリをプロジェクト参照として追加できるはずだ。するとリスト7同様に、.NET Core 1.0のコンソールプロジェクトにクラスライブラリのメソッドを呼び出す処理を追加できるようになる。
■
今回は、クラスライブラリプロジェクトを.NET Standard 2.0をターゲットとして作成し、プロジェクト参照としてコンソールプロジェクトに追加する方法を説明した。さらに複数ターゲットをクラスライブラリに指定する方法を紹介した。
しかし、プロジェクト参照は参照される側のソースコードが必要であり、全てをビルドし直さないといけないなど制約も多い。そこで次回は、今回作成したクラスライブラリプロジェクトをパッケージングし、nuget.org
にアップロードし、コンソールプロジェクトの参照をNuGet経由に変更してみる予定である。
※以下では、本稿の前後を合わせて5回分(第3回~第7回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
3. .NET Coreでプロジェクトを作成して開発してみよう
クロスプラットフォームで開発できる.NET Coreの基礎から開発実践までが学べる入門連載。3回目は実際にプロジェクトを新規に作成して、Visual Studio Codeを使って開発するフローを説明する。
4. .NET Coreでコンソールアプリを配置する
クロスプラットフォームで開発できる.NET Coreの基礎から開発実践までが学べる入門連載。4回目は作成したコンソールアプリのプロジェクトをビルドして配置する手順を説明する。
5. 【現在、表示中】≫ .NET Standardなライブラリプロジェクトを作成して参照する
クロスプラットフォームで開発できる.NET Coreの基礎から開発実践までが学べる入門連載。5回目は.NET Standardなクラスライブラリなプロジェクトを作成し、別のコンソールプロジェクトから参照する方法を説明する。
6. .NET CoreライブラリプロジェクトをパッケージングしてNuGetサーバーに発行する
クロスプラットフォームで開発できる.NET Coreの基礎から開発実践までが学べる入門連載。6回目はクラスライブラリプロジェクトをNuGetパッケージとして参照できるように、作成と発行を行う。
7. .NET Coreで単体テストを行う
クロスプラットフォームで開発できる.NET Coreの基礎から開発実践までが学べる入門連載。最終回は単体テスト用のプロジェクトを作成して、テストを行う方法を説明する。