最初に
本記事はC#9.0に関する情報を自分なりにまとめて書いたものとなります。
普段C#をメインで使用している人もUnityを使ってゲーム開発している人にも、これからのC#に関する予習となれば幸いです。
あとこれアドカレ記事なんですが、完全に遅刻してます、すいません。
↓前半は以下から↓
https://qiita.com/4_mio_11/items/bbd0a7c8800b40a13df9
C#9.0について
現在C#9.0はまだまだ計画段階であり、現在35の機能提案が行われています。
以下でC#の言語機能の提案・計画を確認することができます。
全てがC#9.0に確実に入るというわけではないのですが、この中でもいくつか気になる機能の方をピックアップして軽く紹介したいと思います。
ざっくり下記の機能について見て行きます。
- Records【前半】
- 判別共用体(Discriminated Union)【後半】
- null合体演算子での暗黙的変換【前半】
- Caller Expression Attribute【後半】
- 配列作成時における型の省略化【後半】
- 引数のnullチェック簡略化【前半】
このほかにも細かな構文の変更が提案されていますが、それは追々リリースされてからで…。
判別共用体(Discriminated Union)
こちらはF#の判別共用体やC++のVARIANT型と似ている機能ですが、僕はF#を知らないのでどういった機能になるかと言うところから確認していきたいと思います。
(https://github.com/dotnet/csharplang/issues/113)
判別共用体は、値に対する任意の型の候補を複数定義した型を作成することができる機能だと思われます。
まずはint
型をbool
型どちらかの値を取りうるIntOrBool
型を定義します。その次に、IList<int>
型とタプル、IntOrBool
型いずれかの値を取りうるMixedType
型を定義してみます。
enum class IntOrBool { int I; bool B; }
enum class MixedType
{
(int, int) T;
IList<int> L;
IntOrBool U;
}
判別共用体のインスタンスを作成する場合は、以下のようになります。
// listにはIList<int>型の値が入る
MixedType list = new MixedType.L(new List<int> { 1, 2 });
// MixedType型のtupleに対して(1, 1)というタプル値が入る
var tuple = new MixedType.T(1, 1);
// MixedType型のbResultに対してbool型のtrueという値が入る
var bResult = new MixedType.U( B true);
この判別共用体の値でのパターンマッチング例は以下のようになります。
var x = tuple match (
// tupleに入っている値がTのものなら (1,1) → 1+1 つまり2をxへ
case T (x, y) : x + y,
case U intOrBool when intOrBool is int i : i,
case * : 0
);
Caller Expression Attribute
新しい属性[CallerArgumentExpression]
を使用することで、メソッドに渡される式を文字列としてキャプチャしてエラーメッセージを出力する際にその式の情報も含めることができるようになります。主にAPI開発者などの役に立ちそうな機能です。コードは以下のmdから…。
(https://github.com/dotnet/csharplang/blob/master/proposals/caller-argument-expression.md)
[CallerArgumentExpression("引数名")] string 変数名
と指定することで、指定した引数に渡された式を文字列化して変数名へ格納します。この変数名は引数名+Expressionとすると分かりやすいかもしれません。
public static class Verify
{
public static void InRange(
int argument,
int low,
int high,
[CallerArgumentExpression("argument")] string argumentExpression = null,
[CallerArgumentExpression("low")] string lowExpression = null,
[CallerArgumentExpression("high")] string highExpression = null
)
{
if (argument < low)
{
throw new ArgumentOutOfRangeException(paramName: argumentExpression, message: $ " {argumentExpression} ({argument}) cannot be less than {lowExpression} ({low}).");
}
if (argument > high)
{
throw new ArgumentOutOfRangeException(paramName: argumentExpression, message: $ "{argumentExpression} ({argument}) cannot be greater than {highExpression} ({high}).");
}
}
public static void NotNull<T>(T argument, [CallerArgumentExpression("argument")] string argumentExpression = null)
where T : class
{
if (argument == null) throw new ArgumentNullException(paramName: argumentExpression);
}
}
T ElementAt(this T[] array, int index)
{
Verify.NotNull(array);
// message: "index (-1) cannot be less than 0 (0).", or
// "index (6) cannot be greater than array.Length - 1 (5)."
Verify.InRange(index, 0, array.Length - 1);
return array[index];
}
配列作成時における型の省略化
配列作成時に、new()
式を使用することで、コードの簡略化が可能です。
(https://github.com/dotnet/csharplang/issues/100)
// 簡略化前
IEnumerable<KeyValuePair<string, int>> Headers = new[]
{
new KeyValuePair<string, string>("Hoge", 1),
new KeyValuePair<string, string>("Piyo", 3),
}
// 簡略化後
IEnumerable<KeyValuePair<string, string>> Headers = new KeyValuePair<string, string>[]
{
new("Hoge", 1),
new("Piyo", 3),
}
// 更に簡略化後
IEnumerable<KeyValuePair<string, string>> Headers = new[]
{
new("Hoge", 1),
new("Piyo", 3),
}
この機能提案は賛否両論という感じです。
推論であれば既にvar
が存在していますが、var
を使う場合には型は右辺に明記することとなります。今回のnew()
式ですと、左辺にて型を明記する必要があり、読み辛さを感じる人が多いのかなと。
まとめ
前半と後半に分けて気になる機能の紹介を行いましたがいかがだったでしょうか。数年前から関数型言語が流行ってきていることもあり、Haskellでのような考え方を取り入れることが多いのかなという印象です。ちょっとこれを機に関数型言語の勉強をしようと思います…。実際理解するまでめちゃくちゃ時間かかりました…。
これからのC#の未来に期待しつつ、アドベントカレンダーを締めようと思います、ありがとうございました。