連載
» 2022年01月27日 05時00分 公開

「C# 10」の新機能で、コーダーにうれしいこと8選.NET 6移行入門(2)

.NET 6の現状を把握し、具体的な移行方法を学ぶ連載。今回は、C# 10の新機能について。

[鈴木友宏,NTTデータ先端技術株式会社]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

 .NET 6の現状を把握し、具体的な移行方法を学ぶ本連載「.NET 6移行入門」。第2回は、C# 10の新機能の中から注目の機能をピックアップして紹介します。

 C# 10の新機能の特長は、「簡潔なコードを書くための機能」が多く追加されていることです。新しい機能を利用することで冗長な構文のコードを排除でき、簡潔で直感的なコードを記述できます。

レコード構造体

 C# 9で「レコードクラス」が追加されましたが、C# 10では「レコード構造体」が追加されました。メンバーの数が少ない軽量オブジェクトの場合にクラスよりも速度が出る構造体のレコードを利用できます。

導入の意図

 レコード型(レコード構造体、レコードクラス)は、簡潔なコードで便利にデータを格納するための型です。通常のクラスや構造体でデータモデルを作成しようとすると、データとしての等価性をサポートする「Equals」のオーバーライドや「operator ==」のオーバーロード、表示用の書式設定の「ToString」のオーバーライドやデータを不変にする構文などを記述する面倒な手間が生じます。

 レコード型を利用すると、不変なプロパティを持つ型を簡潔な構文で作成できます。また、上記のようなデータ指向の型に役立つ動作が組み込みでサポートされているので、データモデルを簡潔に定義できます。

値の等価性

 レコード構造体(レコードクラスも含む)は等価性に特徴があります。

  • record class型の場合、型が同じで、同じ値が格納されていれば、2つのオブジェクトは等しい
  • record struct型の場合、型が同じで、同じ値が格納されていれば、2つのオブジェクトは等しい

 比較として、レコード型ではないクラスや構造体の等価性は下記の通りです。

  • class型の場合、メモリ内の同じオブジェクトを参照していれば、2つのオブジェクトは等しい
  • struct型の場合、型が同じで同じ値が格納されていれば、2つのオブジェクトは等しい
// レコード構造体
PersonStruct person01 = new("太郎", "山田", new DateOnly(1990, 4, 15));
PersonStruct person02 = new("太郎", "山田", new DateOnly(1990, 4, 15));
 
Console.WriteLine("レコード構造体");
Console.WriteLine(person01 == person02);
 
// レコードクラス
PersonClass person03 = new("太郎", "山田", new DateOnly(1990, 4, 15));
PersonClass person04 = new("太郎", "山田", new DateOnly(1990, 4, 15));
 
Console.WriteLine("レコードクラス");
Console.WriteLine(person03 == person04);
 
record struct PersonStruct(string FirstName, string LastName, DateOnly Birthday);
record class PersonClass(string FirstName, string LastName, DateOnly Birthday);
コード
レコード構造体
True
-----------
レコードクラス
True
実行結果

表示用の組み込みの書式設定

 レコード型ではToStringメソッドで、パブリックプロパティとフィールドの名前と値が表示されます。データでは値を確認する機会が多いので、この機能はとても便利です。

<record type name> { <property name> = <value>, <property name> = <value>, ...}
PersonStruct person01 = new("太郎", "山田", new DateOnly(1990, 4, 15));
Console.WriteLine(person01);
 
record struct PersonStruct(string FirstName, string LastName, DateOnly Birthday);
コード
PersonStruct { FirstName = 太郎, LastName = 山田, Birthday = 4/15/1990 }
実行結果

with式

 「with式」を使うと、元のインスタンスの値はそのままに、指定したプロパティとフィールドが変更されたコピーを作成できます。膨大なプロパティを持つレコード型の一部のプロパティだけを変更したコピーを簡潔な構文で作成できるので非常に便利です。

 補足ですが、C# 10以降ではレコード型ではない、struct型もwith式をサポートします。

PersonStruct person01 = new("太郎", "山田", new DateOnly(1990, 4, 15));
PersonStruct person02 = new("太郎", "山田", new DateOnly(1990, 4, 15));
 
person02 = person01 with { FirstName = "花子" };
Console.WriteLine(person02);
Console.WriteLine(person01 == person02);
 
record struct PersonStruct(string FirstName, string LastName, DateOnly Birthday);
コード
PersonStruct { FirstName = 花子, LastName = 山田, Birthday = 4/15/1990 }
False
実行結果

 なお、レコード構造体はC# 9のクラスベースのレコードと似ていますが、下記の違いがあります。

  • レコード構造体のプロパティは、デフォルトで変更可能(get/set)
  • レコードクラスのプロパティは、デフォルトでは不変(get/init)

※初期化後にinitキーワードが設定されているプロパティを再割り当てしようとすると、コンパイルエラーが発生します。

※readonlyキーワードを追加することで、レコード構造体を不変にできます。

 レコード構造体はレコードクラスに取って代わるものではなく、レコードクラスからレコード構造体への移行を推奨するものではありません。そのため、クラスと構造体のどちらのレコードを使用するかは、メンバーの数やインスタンスが作成される回数などを考慮し、利用シナリオに合わせて選択する必要があります。

公式情報

global using

 任意のソースファイルに「global using」ディレクティブを追加するか、プロジェクトファイル(*.csproj)に「Using Item」を追加することで、全てのソースファイルで使用したい名前空間を、各ソースファイルで宣言されているかのように指定することができます。

 なお、static修飾子やエイリアスと一緒に使うことができます。

導入の意図

 共通のusing宣言を1カ所に集約できるので、多くのusing行が不要になり、ソースコードからロジック以外の要素を排除できます。ひと言で言えば「ソースコードを簡潔にする機能」です。

global using System.Linq; 
global using static System.Console; // static 併用
global using E = System.Environment; // エイリアス併用
任意のソースファイルに global using ディレクティブを追加する場合
<ItemGroup>
  <Using Include="System.Linq" />
  <Using Include="System.Console" Static="True" /> 
  <Using Include="System.Environment" Alias="E" />
</ItemGroup>
プロジェクトファイル(*.csproj)に Using Itemを追加する場合

公式情報

暗黙的なusingディレクティブ

 コンパイラによって、プロジェクトの種類ごとにあらかじめ決められた、よく使われる名前空間のセットが自動的に追加されます。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。