C#6.0で追加された新たな機能について学習する。
成果物
情報源
一覧
プロジェクト作成
dotnet new console -o cs6 cd cs6
Program.cs
using System; namespace cs6 { class Program { static void Main(string[] args) { new CS6_0().Run(); new CS6_1().Run(); new CS6_2().Run(); new CS6_3().Run(); new CS6_4().Run(); new CS6_5().Run(); new CS6_6().Run(); new CS6_7().Run(); new CS6_8().Run(); } } }
自動プロパティ(読取専用)
using System; namespace cs6 { class CS6_0 { public void Run() { var p = new Person("Bill", "Wagner"); Console.WriteLine("The name, in all caps: " + p.AllCaps()); Console.WriteLine("The name: " + p); } } public class Person { public string FirstName { get; private set; } public string LastName { get; private set; } public Person(string first, string last) { FirstName = first; LastName = last; } public override string ToString() { return FirstName + " " + LastName; } public string AllCaps() { FirstName = FirstName.ToUpper(); LastName = LastName.ToUpper(); return ToString(); } } }
$ dotnet run
The name, in all caps: BILL WAGNER
The name: BILL WAGNER
- プロパティの
set
にprivate
アクセス修飾子を付与することで読取専用プロパティが作れるようになった
もうプロパティ用の変数を宣言する必要はない。
自動プロパティ(初期化)
CS_0.csの対象箇所を以下のように追加・変更する。
CS_1.cs
public string MiddleName { get; } = ""; public Person(string first, string middle, string last) { FirstName = first; MiddleName = middle; LastName = last; }
プロパティ宣言箇所で= ""
のように値を代入できる構文が使えるようになった。
式形式メンバ
CS_1.csの対象箇所を以下のように追加・変更する。
CS_2.cs
public override string ToString() => FirstName + " " + LastName;
ラムダ式をメンバとして宣言できるようになった。以前までは以下のように関数としてしか宣言できなかった。
public override string ToString() { return FirstName + " " + LastName; }
単一クラスのインポート
using static System.Console;
using System; using static System.Console; namespace cs6 { class CS6_3 { public void Run() { System.Console.WriteLine("CS6_3"); Console.WriteLine("CS6_3"); WriteLine("CS6_3"); // 単一クラスのインポートでここまで省略できるようになった。 } } }
略記用っぽい。インポートはSystem
全体。たとえConsole
しか使っていなくとも。
向上した文字列形式
public override string ToString() => $"{FirstName} {LastName}";
ようするに$"{変数名}"
のような文字列補完のこと。標準出力だけでなく文字列生成としても使えたらしい。
var p = new Person("Bill", "Wagner"); WriteLine($"The name, in all caps: {p.AllCaps()}"); WriteLine($"The name is: {p}");
:
以降に書式を指定することもできる。
using System.Linq; var phrase = "the quick brown fox jumps over the lazy dog"; var wordLength = from word in phrase.Split(' ') select word.Length; var average = wordLength.Average(); WriteLine(average); WriteLine($"The average word length is: {wordLength.Average()}"); WriteLine($"The average word length is: {wordLength.Average():F2}");
nullチェック
s
がnull
だと例外が発生する。
string s = null; Console.WriteLine(s.Length); // NullReferenceException
それを回避するためにif
でnullチェックする。面倒!
string s = null; if (null != s) Console.WriteLine(s.Length);
?
でnull
を返すようにできる。
string s = null; Console.WriteLine(s?.Length); // 左オペランドがnullなら?はnullを返す
?
はメソッドの末尾にも付与できる。
bool hasMore = s?.ToCharArray()?.GetEnumerator()?.MoveNext();
??
(null合体演算子)でnull
時の値を返す。
bool hasMore = s?.ToCharArray()?.GetEnumerator()?.MoveNext() ?? false; Console.WriteLine(hasMore);
??
は以下のような三項演算子の糖衣構文みたいなもの。
string s2 = (s == null) ? "Default" : s;
三項演算子をif
文にすると以下。つまり??
は以下をs2 = s ?? "Default"
と表記できる。
if (s == null) s2 = "Default"; else s2 = s;
?
はNullReferenceException
例外を発生させずnull
を返す??
はnull
時の初期値を設定するときの糖衣構文として重宝する
?
, ??
は10億ドルの損失であるnull
への負荷を軽減するための構文である。
例外フィルタ
catch
にwhen
句を追加することでフィルタリングする。
catch (Exception e) when (LogException(e)) {}
using System; namespace cs6 { class CS6_6 { public void Run() { try { string s = null; Console.WriteLine(s.Length); } catch (Exception e) when (LogException(e)) {} Console.WriteLine("Exception must have been handled"); } private bool LogException(Exception e) { Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}"); Console.WriteLine($"\tMessage: {e.Message}"); return false; } } }
nameof
nameof
は変数や型の名前を文字列として返す。
Console.WriteLine(nameof(System.String)); // String int j = 5; Console.WriteLine(nameof(j)); // j List<string> names = new List<string>(); Console.WriteLine(nameof(names)); // names
オブジェクト初期化構文
オブジェクト初期化子の構文で、プロパティとフィールドだけでなく "インデクサー" の初期化がサポートされるようになった。
var messages = new Dictionary<int, string> { [404] = "Page not Found", [302] = "Page moved, but left a forwarding address.", [500] = "The web server can't come out to play today." }; Console.WriteLine(messages[302]);
コレクションクラスのAddメソッドは拡張メソッドでもかまわなくなった。
public class Path : IEnumerable<Point3D> { private List<Point3D> points = new List<Point3D>(); public IEnumerator<Point3D> GetEnumerator() => points.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => points.GetEnumerator(); public void Add(Point3D pt) => points.Add(pt); } public static class Extensions { public static void Add(this Path path, double x, double y, double z) => path.Add(new Point3D(x, y, z)); }
だが上記はコンパイルエラーになる。
CS6_8.cs(25,38): error CS0246: 型または名前空間の名前 'Point3D' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)。 [/tmp/work/CSharp.6.0.20191018093610/src/cs6/cs6.csproj]
名前空間: System.Windows.Media.Media3D Assembly: PresentationCore.dll
using System.Windows.Media.Media3D;
CS6_8.cs(25,38): error CS0246: 型または名前空間の名前 'Point3D' が見つかりませんでした (using ディレクティブまたはアセンブリ参照が指定されていることを確認してください)。 [/tmp/work/CSharp.6.0.20191018093610/src/cs6/cs6.csproj]
最後の最後でドキュメントが説明不足。アセンブリ参照の仕方はまだ説明を受けていない。どうやるの?
$ dotnet --help
...
add .NET プロジェクトにパッケージまたは参照を追加します。
...
使用法: dotnet add <PROJECT> package [options] <PACKAGE_NAME> 引数: <PROJECT> 操作するプロジェクト ファイル。ファイルを指定しない場合、コマンドによって現在のディレクトリから検索されます。 <PACKAGE_NAME> 追加するパッケージ参照。 オプション: -h, --help コマンド ラインのヘルプを表示します。 -v, --version <VERSION> 追加するパッケージのバージョン。 -f, --framework <FRAMEWORK> 特定のフレームワークを対象とする場合にのみ参照を追加します。 -n, --no-restore 復元のプレビューや互換性チェックを行わずに参照を追加します。 -s, --source <SOURCE> 復元中に使用する NuGet パッケージ ソース。 --package-directory <PACKAGE_DIR> パッケージの復元先のディレクトリ。 --interactive コマンドを停止して、ユーザーの入力またはアクション (認証の完了など) を待機できるようにします。
$ dotnet add package PresentationCore.dll Writing /tmp/tmpOOPJKF.tmp info : パッケージ 'PresentationCore.dll' の PackageReference をプロジェクト '/tmp/work/CSharp.Classes.20191018082123/src/classes/classes.csproj' に追加しています。 info : /tmp/work/CSharp.Classes.20191018082123/src/classes/classes.csproj のパッケージを復元しています... info : GET https://api.nuget.org/v3-flatcontainer/presentationcore.dll/index.json info : NotFound https://api.nuget.org/v3-flatcontainer/presentationcore.dll/index.json 785 ミリ秒 error: パッケージ PresentationCore.dll が見つかりません。ソース nuget.org には、この ID のパッケージが存在しません。 error: パッケージ 'PresentationCore.dll' はプロジェクト '/tmp/work/CSharp.Classes.20191018082123/src/classes/classes.csproj' の 'all' フレームワークとの互換性がありません。
PresentationCore.dll が見つかりません。
ということで動作させられなかった。
原因はおそらく、PresentationCore.dll
はグラフィクス関係APIであり、.NETにおいてグラフィクスAPIをサポートしているのはWindowsのみだからだと思われる。Linuxでは使えない……。
環境依存なコードをチュートリアルに使うな! Linuxもサポートしてくれ! Windows支配反対!
所感
最後にモヤモヤさせられて不完全燃焼。Linuxでも使えるとか嘘やんって思ってしまう。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux