- 原文: Introducing C# 10
- 著者: Ken Bonny
- 翻訳: Rwing
- 訳文: [翻訳] C# 10 の新機能をプレビュー
今週前半(訳注:原文は5月1日投稿)、私は Mads Torgersen 氏(Microsoft の C# 言語のチーフデザイナー)が DotNet SouthWest カンファレンスで行った講演を視聴しました。彼は C# 10 に含まれる予定のいくつかのクールな新機能の概要を説明しました。さっそく見ていきましょう。
小さな免責事項:これらの変更のほとんどはすでにほぼ完了しています。しかし、まだ活発に開発中であるため、C# 10 のリリース時にすべてが完全にその通りになるとは保証できません。
struct record
彼が最初に話したことは、現在 record の実装は class を基本オブジェクトとして使用している点です。今後は record struct のバリアントも利用可能になり、基本型を値型にできるようになります。違いは、通常の record は関数間で参照を渡すのに対し、record struct は値のコピーを渡すことです。record struct も with 演算子をサポートします。
また、record に演算子を追加することも可能になり、両方の record 型で利用できます。
record Person(string Name, string Email)
{
public static Person operator +(Person first, Person second)
{
// ロジックをここに記述
}
}
required
C# チームが注力している目標のひとつは、オブジェクトの初期化をより簡単にすることです。そのため、class、struct、record、struct record のプロパティに required マークを付けられるようになります。これにより、それらのプロパティが必須になります。これはコンストラクタまたはオブジェクト初期化子を通じて行うことができます。以下の2つのクラス定義は同等です。required キーワードを追加すると、Name プロパティを設定せずに Person をインスタンス化することはできません。コンパイラがエラーを出し、コンパイルできなくなります。
class Person
{
public required string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
class Person
{
public Person(string name) => Name = name;
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
field
プロパティのさらなる改善として、バッキングフィールドを完全に排除できるようになります。新しいキーワード field が前述のフィールドへのアクセスを提供します。setter および init only プロパティの両方で使用できます。
class Person
{
public string Name { get; init => field = value.Trim(); }
public DateTime DateOfBirth { get; set => field = value.Date; }
}
with
次のバージョンでは、他にもいくつかの面白い小さな改良があります。そのうちの1つは、匿名型も with 演算子をサポートすることです。
var foo = new
{
Name = "Foo",
Email = "foo@mail.com"
};
var bar = foo with {Name = "Bar"};
namespace
名前空間インポートを含むファイルを作成し、そのインポートをどこでも使用できるようになります。例えば、Microsoft.Extensions.Logging.ILogger のように、ほとんどのファイルで使用される非常に一般的な名前空間がある場合、任意の .cs ファイル(Program.cs や専用の Imports.cs をお勧めします)に global using Microsoft.Extensions.Logging.ILogger という行を追加すると、その名前空間がプロジェクト全体で使用できるようになります。これはソリューション全体には適用されないことに注意してください!どのようなインポートが必要になるかを予測できる人はいないため、これらはプロジェクトごとにグループ化されます。
// 訳注:原文にはコード例はありませんが、理解を助けるために独自に追加しました
// Program.cs ファイル
global using System;
// Sample.cs ファイル
// using System; は不要になります
Console.WriteLine("foo");
さらに、名前空間自体にも最適化が加えられます。現在、名前空間はコードを中括弧 で囲む必要があり、そのためすべてのコードが少なくとも1回インデントされます。タブ(またはスペース4つ)と画面スペースを節約するために、ファイル内の任意の場所に名前空間を追加すると、そのファイル内のすべてのコードがその名前空間に属するようになります。研究によると、ほとんどの場合、ファイル内のすべてのコードは同じ名前空間に属します。この方法によりファイルサイズが削減されます。これはソリューション全体では目立たないかもしれませんが(たとえ何千ものファイルが含まれていても)、GitHub/GitLab/BitBucket などの規模では、いくらかのスペースを節約できるでしょう。もし1つのファイルに複数の名前空間を含めたい場合は、中括弧を使用する従来の方法も引き続き利用可能です。
// 従来の方法 LegacyNamespace.cs
namespace LegacyNamespace
{
class Foo
{
// レガシーコードをここに記述
}
}
// 簡略化された方法 SimplifiedNamespace.cs
namespace SimplifiedNamespace;
class Bar
{
// 素晴らしいコードをここに記述
}
lambda
ラムダ式にもいくつかのクールなアップデートがあります。コンパイラはラムダシグネチャの推論をより良くサポートし、さらに属性を追加できるようになります。明示的に戻り値の型を指定して、コンパイラがラムダを理解するのを助けることもできます。
var f = Console.WriteLine;
var f = x => x; // 戻り値の型を推論
var f = (string x) => x; // シグネチャを推論
var f = [NotNull] x => x; // 属性を追加
var f = [NotNull] (int x) => x;
var f = [return: NotNull] static x => x; // 属性を追加
var f = T () => default; // 明示的な戻り値の型
var f = ref int (ref int x) => ref x; // ref を使用
var f = int (x) => x; // 暗黙的な引数の戻り値の型を明示
var f = static void (_) => Console.Write("Help");
Schooley 氏に、よりわかりやすい機能の例を提案してもらいました。感謝します。
interface
最後に、インターフェイス上で静的メソッドやプロパティを指定できるようになります。これは、インターフェイスにデフォルト実装を追加したときと同様に、議論を呼ぶトピックになることは承知しています。私はあまり好きではありません。しかし、これは非常に面白いかもしれません。インターフェイスのデフォルト値や作成メソッドを指定できると想像してみてください。
interface IFoo
{
static IFoo Empty { get; }
static operator +(IFoo first, IFoo second);
}
class Foo : IFoo
{
public static IFoo Empty => new Foo();
public static operator +(IFoo first, IFoo second) => /* 計算をここに記述 */;
}
個人的には、これらの変更は好きです。特に気に入ったのは名前空間の変更とインターフェイスの改良です。ともあれ、C# の未来は明るいです。うーん…(訳注:ここで著者は言葉遊びをしています。原文の "the future is seeing sharp, see sharp" は C# の発音に似ています)
皆さん、ありがとうございました。それではまた。