21. C# 9.0のnull解析: 制約なしT?
• 制約なしT? は「defaultable」
public T? M() => default;
戻り値はdefault
• 参照型 : null
• null許容値型 : null
• 非null値型 : 0
string? a = new A<string?>().M();
string? b = new A<string>().M();
int? c = new A<int?>().M();
int d = new A<int>().M();
参照型の時は
string → string?
非null値型の時は
int → int (0が返る)
これはnullが返る
要はLINQのFirstOrDefaultと同じ挙動
23. Working with data
• データが主役のプログラミング
class Point
{
public int X { get; }
public int Y { get; }
}
• 保存とか通信とかするとき
こういうpublicプロパティだ
け持つ型を多用
• immutableにしたい
• value semanticsを持ちたい
24. value semantics
• (参照型であっても)値で比較
• コピーを作って部分書き換え
var x = new Point(1, 2);
var y = new Point(1, 2);
Console.WriteLine(x == y); true
var x = new Point(1, 2);
var y = x with { X = 3 };
x は {X=1, Y=2} のまま変えない
新たに {X=3, Y=2} で y を作る
25. Working with dataのボイラープレート
• 真面目に書こうとすると煩雑
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
immutableにしたいとき、こ
の手のボイラープレート
コードが多い問題
この他にもEqualsとか
GetHashCodeとか
Deconstructとか
同じ名前を何か所も…
26. Working with dataのための言語機能
• 言語機能のサポートが欲しい
• パターン マッチングとかレコード型とかはこの一環
class Point
{
public int X { get; }
public int Y { get; }
}
record Point(int X, int Y);
• ボイラープレートコード削減
• 推奨する方式(immutable)を一
番短く書けるように
30. C# 9.0で追加されるパターン
• 組み合わせ&大小比較(網羅チェックがかかる)
変化後
int m(byte b) => b switch
{
< 10 => 1,
>= 10 and < 100 => 2,
>= 100 => 3,
_ => -1
};
読みやすい・書きやすいか
というと微妙なものの…
ここをエラーにしてくれる
この3条件でbyteが取り
うる値を全部網羅してる
なので到達不能
31. パターン補足: not
• not null
x is not null
x is { }
x != null
一応、新パターン
一番「意味通り」なコードに
C# 8.0のプロパティ パターンで代用
プロパティ値取得前にnullチェックが挟まる
ユーザー定義 == があるときに意味が変わる
(ユーザー定義のやつが呼ばれる)
(not nullは常に参照比較)
32. パターン補足: not
• notと変数宣言
void m(object x)
{
if (x is not string s) return;
// else の側で s を使える
Console.WriteLine(s.Length);
}
notの後ろで変数宣言可能
(switchでは不可。if限定)
33. パターン補足: and
• and両辺の変数宣言はどちらもdefinite assigned
interface IA { int X { get; } }
interface IB { int Y { get; } }
int m(object x) => x switch
{
IA a and IB b => a.X * b.Y,
};
aもbも確実に初期化されてる保証あり
プロパティ アクセス可能
34. パターン補足: or
• orは一応、共通型判定を受ける
object m(object x) => x switch
{
(IEnumerable or ICollection) and var e => e.GetEnumerator(),
};
IEnumerableとICollectionの
共通型を一応調べてくれる
この場合、eはIEnumerable
(ただ、C#の「共通型」判定自体が
そんなに賢くないのでそんなに役立たない)
36. record
• value semanticsを持つ参照型
record Point
{
public int X { get; }
public int Y { get; }
}
コンパイラー生成
bool Equals(Point other) => (X, Y) == (other.X, other.Y);
int GetHashCode() => HashCode.Combine(X, Y);
void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
Clone, ToString, IEquatable<T>, ...
(デフォルトで)
publicなプロパティ
全部が関与
39. initプロパティ
• new直後?
• 要するにオブジェクト初期化子
var p = new Point
{
X = 1,
Y = 2,
};
p.X = 1;
var p = new Point
p.X = 1;
p.Y = 2;
コンパイラー
による展開
展開結果は同じだけど
コンパイル時検査で
「初期化のみ」を保証
40. initプロパティ
• Clone直後?
• with式: Clone + 部分書き換え
var p1 = new Point
{
X = 1,
Y = 2,
}
var p2 = p1 with { X = 3 };
p1は(1, 2)のまま
p2は(3, 2)に
41. initプロパティ
• Clone直後?
• with式: Clone + 部分書き換え
var p2 = p1.Clone();
p2.X = 3;
var p2 = p1 with { X = 3 };
Clone直後だけ書き換え可能
(これもコンパイル時検査)
※
※ 実際には<Clone>$みたいな名前で生成されてて
with式以外からは呼べないようにしてある
42. プライマリ コンストラクター
• 型名直後に引数リスト
• public initプロパティとコンストラクターを生成
record Point(int X, int Y);
record Point
{
public int X { get; init; }
public int Y { get; init; }
public Point(int x, int y) => (X, Y) = (x, y);
}
43. プライマリ コンストラクター
• 現状、record専用の構文
record Point(int X, int Y);
C# 9.0
class Point(int X, int Y) { }
C# 10.0
struct Point(int X, int Y) { }
• プロパティ生成まではしない
• メンバー初期化子で参照できる
47. 名前付きの型と匿名の型
• 名前の有無での差を減らしたい
匿名
(anonymous)
ソース型推論 ターゲット型推論
(target-typed new)
位置指定
(positional)
var x = (1, 2); var x = new T(1, 2); T x = new(1, 2);
名前指定
(nominal)
var x = new
{
X = 1,
Y = 2,
};
var x = new T
{
X = 1,
Y = 2,
};
T x = new()
{
X = 1,
Y = 2,
};
匿名型(C# 3.0)
タプル(C# 7.0) ターゲットからの型推論(C# 9.0)
オブジェクト初期化子(C# 3.0)
initプロパティ(C# 9.0)
48. target-typed new
• varと逆向きに型推論
• varが使えない場所と言えば…
class T
{
Dictionary<string, List<(int x, int y)[]> d = new();
}
多分これが一番よく使うと思います
49. discriminated union (C# 10.0予定)
• recordをdiscriminated union的にしたい
record Shape
{
Circle(float R);
Rect(float W, float H);
}
float Area(Shape s) => s switch
{
Circle c => c.R * c.R * MathF.PI,
Rect r => r.W * r.H,
};
※ 多分、別キーワードか
追加修飾子が必要
(これ自体はC# 9.0でも書ける)
「CircleとRectしかない」(網羅性)
の判定が9.0だとできない