C# 9.0 の新機能What's new in C# 9.0

C# 9.0 によって、C# 言語に次の機能と機能強化が追加されています。C# 9.0 adds the following features and enhancements to the C# language:

  • レコードRecords
  • init 専用セッターInit only setters
  • 最上位レベルのステートメントTop-level statements
  • パターン マッチングの拡張機能Pattern matching enhancements
  • ネイティブ サイズの整数Native sized integers
  • 関数ポインターFunction pointers
  • localsinit フラグの出力を抑制するSuppress emitting localsinit flag
  • ターゲット型の新しい式Target-typed new expressions
  • 静的な匿名関数static anonymous functions
  • ターゲットにより型指定された条件式Target-typed conditional expressions
  • 共変の戻り値の型Covariant return types
  • foreach ループの拡張機能 GetEnumerator サポートExtension GetEnumerator support for foreach loops
  • ラムダ ディスカード パラメーターLambda discard parameters
  • ローカル関数の属性Attributes on local functions
  • モジュールの初期化子Module initializers
  • 部分メソッドの新機能New features for partial methods

C# 9.0 は .NET 5 でサポートされています。C# 9.0 is supported on .NET 5. 詳細については、「C# 言語のバージョン管理」を参照してください。For more information, see C# language versioning.

レコードの種類Record types

C# 9.0 には "レコード型" が導入されています。これは、等価性の値のセマンティクスを提供するための合成されたメソッドを提供する参照型です。C# 9.0 introduces record types, which are a reference type that provides synthesized methods to provide value semantics for equality. 既定では、レコードは変更できません。Records are immutable by default.

レコード型を使用すると、変更できない参照型を .NET で簡単に作成できます。Record types make it easy to create immutable reference types in .NET. 従来、.NET 型は、参照型 (クラス型と匿名型を含む) と値型 (構造体とタプルを含む) に大別されています。Historically, .NET types are largely classified as reference types (including classes and anonymous types) and value types (including structs and tuples). 変更できない値型が推奨されますが、変更可能な値型でエラーが頻繁に発生するわけではありません。While immutable value types are recommended, mutable value types don’t often introduce errors. 値型の変数には値が保持され、値型がメソッドに渡されるときは、元のデータのコピーが変更されます。Value type variables hold the values so changes are made to a copy of the original data when value types are passed to methods.

変更できない参照型にも多くの利点があります。There are many advantages to immutable reference types as well. これらの利点は、共有データを使用する同時実行プログラムで、より顕著になります。These advantages are more pronounced in concurrent programs with shared data. 残念ながら、C# で変更できない参照型を作成するには、余分なコードをかなり記述する必要がありました。Unfortunately, C# forced you to write quite a bit of extra code to create immutable reference types. レコードにより、等価性の値のセマンティクスを使用する、変更できない参照型の型宣言が提供されます。Records provide a type declaration for an immutable reference type that uses value semantics for equality. 等価コードとハッシュ コードの合成メソッドでは、プロパティがすべて等しい場合、2 つのレコードは等しいと見なされます。The synthesized methods for equality and hash codes consider two records equal if their properties are all equal. 次の定義を考慮してください。Consider this definition:

public record Person
{
    public string LastName { get; }
    public string FirstName { get; }

    public Person(string first, string last) => (FirstName, LastName) = (first, last);
}

レコード定義によって、FirstNameLastName の 2 つの読み取り専用プロパティを含む Person 型が作成されます。The record definition creates a Person type that contains two readonly properties: FirstName and LastName. Person 型は参照型です。The Person type is a reference type. IL を見た場合は、それはクラスです。If you looked at the IL, it’s a class. どのプロパティも作成後に変更できないので、それは変更できません。It’s immutable in that none of the properties can be modified once it's been created. レコード型を定義すると、コンパイラによって他のいくつかのメソッドが自動的に合成されます。When you define a record type, the compiler synthesizes several other methods for you:

  • 値ベースの等価比較のためのメソッドMethods for value-based equality comparisons
  • GetHashCode() のオーバーライドOverride for GetHashCode()
  • コピー メンバーとクローン メンバーCopy and Clone members
  • PrintMembers および ToString()PrintMembers and ToString()

レコードによって、継承がサポートされます。Records support inheritance. 次のようにして、Person の新しい派生レコードを宣言できます。You can declare a new record derived from Person as follows:

public record Teacher : Person
{
    public string Subject { get; }

    public Teacher(string first, string last, string sub)
        : base(first, last) => Subject = sub;
}

また、レコードをシールして、さらに派生させることもできます。You can also seal records to prevent further derivation:

public sealed record Student : Person
{
    public int Level { get; }

    public Student(string first, string last, int level) : base(first, last) => Level = level;
}

コンパイラにより、上記のメソッドの異なるバージョンが合成されます。The compiler synthesizes different versions of the methods above. メソッドのシグネチャは、レコード型がシールされているかどうか、および直接基底クラスがオブジェクトであるかどうかによって異なります。The method signatures depend on if the record type is sealed and if the direct base class is object. レコードには次の機能が必要です。Records should have the following capabilities:

  • 等価性は値に基づいており、型が一致するかどうかのチェックが含まれます。Equality is value-based, and includes a check that the types match. たとえば、2 つのレコードが同じ名前を共有している場合でも、StudentPerson と同じにすることはできません。For example, a Student can't be equal to a Person, even if the two records share the same name.
  • レコードには、自動的に生成される一貫した文字列表現があります。Records have a consistent string representation generated for you.
  • レコードによって、コピーの構築がサポートされます。Records support copy construction. 正しいコピーの構築には、継承階層と、開発者によって追加されたプロパティが含まれる必要があります。Correct copy construction must include inheritance hierarchies, and properties added by developers.
  • レコードは、変更してコピーできます。Records can be copied with modification. これらのコピー操作と変更操作では、非破壊的な変異がサポートされます。These copy and modify operations supports non-destructive mutation.

使い慣れた Equals オーバーロード、operator ==operator != に加えて、コンパイラによって新しい EqualityContract プロパティが合成されます。In addition to the familiar Equals overloads, operator ==, and operator !=, the compiler synthesizes a new EqualityContract property. プロパティからは、レコードの型に一致する Type オブジェクトが返されます。The property returns a Type object that matches the type of the record. 基本データ型が object の場合、プロパティは virtual になります。If the base type is object, the property is virtual. 基本データ型が別のレコード型である場合、プロパティは override になります。If the base type is another record type, the property is an override. レコード型が sealed の場合、プロパティは sealed になります。If the record type is sealed, the property is sealed. 合成された GetHashCode によって、基本データ型とレコード型で宣言されているすべてのプロパティとフィールドの GetHashCode が使用されます。The synthesized GetHashCode uses the GetHashCode from all properties and fields declared in the base type and the record type. これらの合成メソッドにより、継承階層全体で値ベースの等価性が適用されます。These synthesized methods enforce value-based equality throughout an inheritance hierarchy. つまり、Student は、同じ名前の Person と等しいとは見なされません。That means a Student will never be considered equal to a Person with the same name. 2 つのレコードの型が一致し、さらにレコード型の間で共有されているすべてのプロパティが等しい必要があります。The types of the two records must match as well as all properties shared among the record types being equal.

レコードには、合成されたコンストラクターと、コピーを作成するための "clone" メソッドもあります。Records also have a synthesized constructor and a "clone" method for creating copies. 合成コンストラクターは、レコード型の引数を 1 つ持ちます。The synthesized constructor has one argument of the record type. これにより、レコードのすべてのプロパティの値が同じ新しいレコードが生成されます。It produces a new record with the same values for all properties of the record. レコードがシールされている場合、このコンストラクターは private です。それ以外の場合は、protected です。This constructor is private if the record is sealed, otherwise it's protected. 合成された "clone" メソッドによって、レコード階層のコピーの構築がサポートされます。The synthesized "clone" method supports copy construction for record hierarchies. "clone" という用語が引用符で囲まれているのは、実際の名前はコンパイラによって生成されるためです。The term "clone" is in quotes because the actual name is compiler generated. レコード型で Clone という名前のメソッドを作成することはできません。You can't create a method named Clone in a record type. 合成された "clone" メソッドによって、仮想ディスパッチを使用してコピーされるレコードの型が返されます。The synthesized "clone" method returns the type of record being copied using virtual dispatch. コンパイラにより、record のアクセス修飾子に応じて、異なる修飾子が "clone" メソッドに追加されます。The compiler adds different modifiers for the "clone" method depending on the access modifiers on the record:

  • レコード型が abstract の場合は、"clone" メソッドも abstract になります。If the record type is abstract, the "clone" method is also abstract. 基本データ型が object でない場合は、メソッドも override になります。If the base type isn't object, the method is also override.
  • 基本データ型が object であるときの、abstract ではないレコード型の場合:For record types that aren't abstract when the base type is object:
    • レコードが sealed の場合、追加の修飾子は "clone" メソッドに追加されません (つまり、virtual ではありません)。If the record is sealed, no additional modifiers are added to the "clone" method (meaning it is not virtual).
    • レコードが sealed ではない場合、"clone" メソッドは virtual になります。If the record isn't sealed, the "clone" method is virtual.
  • 基本データ型が object ではないときの、abstract ではないレコード型の場合:For record types that aren't abstract when the base type is not object:
    • レコードが sealed の場合は、"clone" メソッドも sealed になります。If the record is sealed, the "clone" method is also sealed.
    • レコードが sealed ではない場合、"clone" メソッドは override になります。If the record isn't sealed, the "clone" method is override.

これらすべてのルールの結果として、レコード型のすべての階層で等価性が一貫して実装されます。The result of all these rules is the equality is implemented consistently across any hierarchy of record types. 次の例で示すように、プロパティが等しく、型が同じである場合、2 つのレコードは互いに等しくなります。Two records are equal to each other if their properties are equal and their types are the same, as shown in the following example:

var person = new Person("Bill", "Wagner");
var student = new Student("Bill", "Wagner", 11);

Console.WriteLine(student == person); // false

コンパイラにより、印刷出力をサポートする 2 つのメソッド ToString() のオーバーライドと PrintMembers が合成されます。The compiler synthesizes two methods that support printed output: a ToString() override, and PrintMembers. PrintMembers は、引数として System.Text.StringBuilder を受け取ります。The PrintMembers takes a System.Text.StringBuilder as its argument. レコード型のすべてのプロパティに対し、プロパティ名と値のコンマ区切りリストが追加されます。It appends a comma-separated list of property names and values for all properties in the record type. PrintMembers によって、他のレコードからのすべての派生レコードに対する基本実装が呼び出されます。PrintMembers calls the base implementation for any records derived from other records. ToString() のオーバーライドでは、PrintMembers によって生成され、{} で囲まれた文字列が返されます。The ToString() override returns the string produced by PrintMembers, surrounded by { and }. たとえば、Student に対する ToString() メソッドでは、次のコードのような string が返されます。For example, the ToString() method for Student returns a string like the following code:

"Student { LastName = Wagner, FirstName = Bill, Level = 11 }"

これまでに示した例では、従来の構文を使用してプロパティが宣言されています。The examples shown so far use traditional syntax to declare properties. "位置指定レコード" と呼ばれる簡潔な形式があります。There's a more concise form called positional records. 次に示すのは、前に位置指定レコードとして定義されている 3 つのレコード型です。Here are the three record types defined earlier as positional records:

public record Person(string FirstName, string LastName);

public record Teacher(string FirstName, string LastName,
    string Subject)
    : Person(FirstName, LastName);

public sealed record Student(string FirstName,
    string LastName, int Level)
    : Person(FirstName, LastName);

これらの宣言によって、以前のバージョンと同じ機能が作成されます (次のセクションで説明する追加の機能がいくつかあります)。These declarations create the same functionality as the earlier version (with a couple extra features covered in the following section). これらのレコードには新しいメソッドが追加されていないため、これらの宣言は角かっこではなくセミコロンで終わっています。These declarations end with a semicolon instead of brackets because these records don't add additional methods. 本体を追加し、追加のメソッドを含めることもできます。You can add a body, and include any additional methods as well:

public record Pet(string Name)
{
    public void ShredTheFurniture() =>
        Console.WriteLine("Shredding furniture");
}

public record Dog(string Name) : Pet(Name)
{
    public void WagTail() =>
        Console.WriteLine("It's tail wagging time");

    public override string ToString()
    {
        StringBuilder s = new();
        base.PrintMembers(s);
        return $"{s.ToString()} is a dog";
    }
}

コンパイラによって、位置指定レコードに対して Deconstruct メソッドが生成されます。The compiler produces a Deconstruct method for positional records. Deconstruct メソッドには、レコード型のすべてのパブリック プロパティの名前と一致するパラメーターがあります。The Deconstruct method has parameters that match the names of all public properties in the record type. Deconstruct メソッドを使用して、レコードをコンポーネント プロパティに分解できます。The Deconstruct method can be used to deconstruct the record into its component properties:

var person = new Person("Bill", "Wagner");

var (first, last) = person;
Console.WriteLine(first);
Console.WriteLine(last);

最後に、レコードによって "with 式" がサポートされています。Finally, records support with-expressions. "with 式" では、レコードのコピーを、with で指定したプロパティを変更して作成するように、コンパイラに指示されます。A with-expression instructs the compiler to create a copy of a record, but with specified properties modified:

Person brother = person with { FirstName = "Paul" };

上の行では、LastName プロパティは person のコピーで、FirstName が "Paul" である、新しい Person レコードが作成されます。The above line creates a new Person record where the LastName property is a copy of person, and the FirstName is "Paul". with 式では、任意の数のプロパティを設定できます。You can set any number of properties in a with-expression. "clone" メソッド以外のすべての合成メンバーは、開発者が自分で記述できます。Any of the synthesized members except the "clone" method may be written by you. レコード型に、いずれかの合成メソッドのシグネチャと一致するメソッドがある場合、コンパイラでそのメソッドは合成されません。If a record type has a method that matches the signature of any synthesized method, the compiler doesn't synthesize that method. 前の Dog レコードの例には、手作業でコーディングされた ToString() メソッドが例として含まれます。The earlier Dog record example contains a hand coded ToString() method as an example.

init 専用セッターInit only setters

"init 専用セッター" によって、オブジェクトのメンバーを初期化するための一貫した構文が提供されます。Init only setters provide consistent syntax to initialize members of an object. プロパティ初期化子を使用すると、どの値によってどのプロパティが設定されているかが明確にされます。Property initializers make it clear which value is setting which property. 欠点は、それらのプロパティが設定可能である必要があることです。The downside is that those properties must be settable. C# 9.0 以降では、プロパティとインデクサーに対して set アクセサーの代わりに init アクセサーを作成できます。Starting with C# 9.0, you can create init accessors instead of set accessors for properties and indexers. 呼び出し元により、プロパティ初期化子構文を使用して作成式でこれらの値を設定することができますが、構築が完了するとそれらのプロパティは読み取り専用になります。Callers can use property initializer syntax to set these values in creation expressions, but those properties are readonly once construction has completed. init 専用セッターによって、状態を変更するためのウィンドウが提供されます。Init only setters provide a window to change state. 構築フェーズが終了すると、そのウィンドウは閉じます。That window closes when the construction phase ends. プロパティ初期化子と with 式の完了を含め、すべての初期化の後で、構築フェーズは実質的に終了します。The construction phase effectively ends after all initialization, including property initializers and with-expressions have completed.

位置指定レコードに関する前の例では、init 専用セッターを使用して、with 式を使用するプロパティを設定する方法が示されています。The preceding example for positional records demonstrates using an init-only setter to set a property using a with expression. init 専用セッターは、記述する任意の型で宣言できます。You can declare init only setters in any type you write. たとえば、次の構造体では、気象監視構造体が定義されています。For example, the following struct defines a weather observation structure:

public struct WeatherObservation
{
    public DateTime RecordedAt { get; init; }
    public decimal TemperatureInCelsius { get; init; }
    public decimal PressureInMillibars { get; init; }

    public override string ToString() =>
        $"At {RecordedAt:h:mm tt} on {RecordedAt:M/d/yyyy}: " +
        $"Temp = {TemperatureInCelsius}, with {PressureInMillibars} pressure";
}

呼び出し元は、プロパティ初期化子構文を使用して値を設定できますが、それでも不変性は維持されます。Callers can use property initializer syntax to set the values, while still preserving the immutability:

var now = new WeatherObservation 
{ 
    RecordedAt = DateTime.Now, 
    TemperatureInCelsius = 20, 
    PressureInMillibars = 998.0m 
};

ただし、初期化後に監視を変更することは、初期化の外側で init 専用プロパティに代入することによるエラーになります。But, changing an observation after initialization is an error by assigning to an init-only property outside of initialization:

// Error! CS8852.
now.TemperatureInCelsius = 18;

init 専用セッターは、派生クラスから基底クラスのプロパティを設定する場合に便利です。Init only setters can be useful to set base class properties from derived classes. また、基底クラスのヘルパーを使用して派生プロパティを設定することもできます。They can also set derived properties through helpers in a base class. 位置指定レコードによって、init 専用セッターを使用してプロパティが宣言されます。Positional records declare properties using init only setters. これらのセッターは、with 式で使用されます。Those setters are used in with-expressions. 定義する任意の class または struct に対して、init 専用セッターを宣言できます。You can declare init only setters for any class or struct you define.

最上位レベルのステートメントTop-level statements

"最上位レベル ステートメント" により、多くのアプリケーションから不要な手続きが削除されます。Top-level statements remove unnecessary ceremony from many applications. 正規の "Hello World!"Consider the canonical "Hello World!" プログラムについて考えます。program:

using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

何かを行うコード行は 1 つだけです。There’s only one line of code that does anything. 最上位レベル ステートメントを使用すると、そのすべての定型句を、using ステートメントと処理を行う 1 行に置き換えることができます。With top-level statements, you can replace all that boilerplate with the using statement and the single line that does the work:

using System;

Console.WriteLine("Hello World!");

1 行だけのプログラムが必要な場合は、using ディレクティブを削除し、完全修飾型名を使用することができます。If you wanted a one-line program, you could remove the using directive and use the fully qualified type name:

System.Console.WriteLine("Hello World!");

最上位レベル ステートメントを使用できるのは、アプリケーション内の 1 つのファイルだけです。Only one file in your application may use top-level statements. コンパイラにより、複数のソース ファイルで最上位レベル ステートメントが検出されると、エラーになります。If the compiler finds top-level statements in multiple source files, it’s an error. また、最上位レベル ステートメントを、宣言されたプログラム エントリ ポイント メソッド (通常は Main メソッド) と組み合わせても、エラーになります。It’s also an error if you combine top-level statements with a declared program entry point method, typically a Main method. ある程度まで、その 1 つのファイルに、通常は Program クラスの Main メソッドに含まれるステートメントが含まれているものと考えることができます。In a sense, you can think that one file contains the statements that would normally be in the Main method of a Program class.

この機能の最も一般的な用途の 1 つは、教材の作成です。One of the most common uses for this feature is creating teaching materials. C# の開発初心者は、正規の "Hello World!" をBeginner C# developers can write the canonical “Hello World!” 1 行または 2 行のコードで作成できます。in one or two lines of code. 余分な手続きは必要ありません。None of the extra ceremony is needed. 一方、経験豊富な開発者は、この機能の多くの用途を見つけることができます。However, seasoned developers will find many uses for this feature as well. 最上位レベル ステートメントを使用すると、Jupyter Notebook で提供されるものと同様の実験用にスクリプトに似たエクスペリエンスを有効にできます。Top-level statements enable a script-like experience for experimentation similar to what Jupyter notebooks provide. 最上位レベル ステートメントは、小規模なコンソール プログラムとユーティリティに最適です。Top-level statements are great for small console programs and utilities. Azure 関数は、最上位レベル ステートメントに最適なユース ケースです。Azure functions are an ideal use case for top-level statements.

最も重要なのは、最上位レベル ステートメントでアプリケーションのスコープや複雑さが制限されないことです。Most importantly, top-level statements don't limit your application’s scope or complexity. それらのステートメントでは、任意の .NET クラスにアクセスしたり、使用したりできます。Those statements can access or use any .NET class. また、コマンド ライン引数や戻り値の使用も制限されません。They also don’t limit your use of command-line arguments or return values. 最上位レベル ステートメントでは、args という名前の文字列の配列にアクセスできます。Top-level statements can access an array of strings named args. 最上位レベル ステートメントで整数値が返される場合、その値は、合成された Main メソッドからの整数のリターン コードになります。If the top-level statements return an integer value, that value becomes the integer return code from a synthesized Main method. 最上位レベル ステートメントには、非同期式を含めることができます。The top-level statements may contain async expressions. その場合、合成されたエントリ ポイントからは Task または Task<int> が返されます。In that case, the synthesized entry point returns a Task, or Task<int>.

パターン マッチングの拡張機能Pattern matching enhancements

C# 9 には、新しいパターン マッチングの機能強化が含まれています。C# 9 includes new pattern matching improvements:

  • "型パターン" は、変数が型である場合に一致しますType patterns match a variable is a type
  • "かっこで囲まれたパターン" では、パターンの組み合わせの優先順位が適用または強調されますParenthesized patterns enforce or emphasize the precedence of pattern combinations
  • "接続的 and パターン" では、両方のパターンが一致することが要求されますConjunctive and patterns require both patterns to match
  • "離接的 or パターン" では、どちらかのパターンが一致することが要求されますDisjunctive or patterns require either pattern to match
  • "否定的 not パターン" では、パターンが一致しないことが要求されますNegated not patterns require that a pattern doesn’t match
  • "関係パターン" では、入力が定数より小さい、より大きい、以下、または以上であることが要求されます。Relational patterns require the input be less than, greater than, less than or equal, or greater than or equal to a given constant.

これらのパターンにより、パターンの構文が豊富になります。These patterns enrich the syntax for patterns. 次のような例を考えてみてください。Consider these examples:

public static bool IsLetter(this char c) =>
    c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';

または、省略可能なかっこを使用して、andor より優先順位が高いことを明確にします。Alternatively, with optional parentheses to make it clear that and has higher precedence than or:

public static bool IsLetterOrSeparator(this char c) =>
    c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';

最も一般的な使用方法の 1 つは、null チェックの新しい構文です。One of the most common uses is a new syntax for a null check:

if (e is not null)
{
    // ...
}

これらのパターンのいずれも、パターンが許可される任意のコンテキスト (is パターン式、switch 式、入れ子になったパターン、switch ステートメントの case ラベルのパターン) で使用できます。Any of these patterns can be used in any context where patterns are allowed: is pattern expressions, switch expressions, nested patterns, and the pattern of a switch statement’s case label.

パフォーマンスと相互運用Performance and interop

3 つの新機能により、高パフォーマンスを必要とするネイティブ相互運用および低レベル ライブラリのサポートが向上します (ネイティブ サイズの整数、関数ポインター、localsinit フラグの省略)。Three new features improve support for native interop and low-level libraries that require high performance: native sized integers, function pointers, and omitting the localsinit flag.

ネイティブ サイズの整数 nintnuint は整数型です。Native sized integers, nint and nuint, are integer types. これらは、基になる型 System.IntPtr および System.UIntPtr によって表されます。They're expressed by the underlying types System.IntPtr and System.UIntPtr. コンパイラによって、これらの型に対する追加の変換と操作がネイティブ int として公開されます。The compiler surfaces additional conversions and operations for these types as native ints. ネイティブ サイズの整数では、MaxValue または MinValue のプロパティが定義されます。Native sized integers define properties for MaxValue or MinValue. これらの値は、ターゲット コンピューターでの整数のネイティブ サイズに依存するため、コンパイル時の定数として表すことはできません。These values can't be expressed as compile time constants because they depend on the native size of an integer on the target machine. これらの値は実行時に読み取り専用になります。Those values are readonly at runtime. nint に対する定数値は、[int.MinValue ..You can use constant values for nint in the range [int.MinValue .. int.MaxValue].int.MaxValue]. nuint に対する定数値は、[uint.MinValue ..You can use constant values for nuint in the range [uint.MinValue .. uint.MaxValue].uint.MaxValue]. コンパイラによって、System.Int32 型と System.UInt32 型を使用するすべての単項演算子と二項演算子に対して、定数の折りたたみが実行されます。The compiler performs constant folding for all unary and binary operators using the System.Int32 and System.UInt32 types. 結果が 32 ビットに収まらない場合、演算は実行時に実行され、定数とは見なされません。If the result doesn't fit in 32 bits, the operation is executed at runtime and isn't considered a constant. ネイティブ サイズの整数を使用すると、整数演算が広く使用されており、最速のパフォーマンスを実現する必要があるシナリオで、パフォーマンスを向上させることができます。Native sized integers can increase performance in scenarios where integer math is used extensively and needs to have the fastest performance possible.

関数ポインターでは、IL オペコード ldftn および calli にアクセスするための簡単な構文が提供されます。Function pointers provide an easy syntax to access the IL opcodes ldftn and calli. 関数ポインターは、新しい delegate* 構文を使用して宣言できます。You can declare function pointers using new delegate* syntax. delegate* 型はポインター型です。A delegate* type is a pointer type. Invoke() メソッドで callvirt が使用されるデリゲートとは異なり、delegate* 型の呼び出しでは calli が使用されます。Invoking the delegate* type uses calli, in contrast to a delegate that uses callvirt on the Invoke() method. 構文的には、呼び出しは同じです。Syntactically, the invocations are identical. 関数ポインターの呼び出しでは、managed の呼び出し規約が使用されます。Function pointer invocation uses the managed calling convention. unmanaged の呼び出し規約が必要であることを宣言するには、delegate* 構文の後に unmanaged キーワードを追加します。You add the unmanaged keyword after the delegate* syntax to declare that you want the unmanaged calling convention. その他の呼び出し規約は、delegate* 宣言の属性を使用して指定できます。Other calling conventions can be specified using attributes on the delegate* declaration.

最後に、System.Runtime.CompilerServices.SkipLocalsInitAttribute を追加することで、localsinit フラグを生成しないようコンパイラに指示することができます。Finally, you can add the System.Runtime.CompilerServices.SkipLocalsInitAttribute to instruct the compiler not to emit the localsinit flag. このフラグは、すべてのローカル変数をゼロで初期化するように CLR に指示します。This flag instructs the CLR to zero-initialize all local variables. 1.0 から、localsinit フラグが C# に対する既定の動作でした。The localsinit flag has been the default behavior for C# since 1.0. しかし、一部のシナリオでは、ゼロによる初期化を追加すると、パフォーマンスに大きく影響する可能性があります。However, the extra zero-initialization may have measurable performance impact in some scenarios. 特に、stackalloc を使用する場合です。In particular, when you use stackalloc. そのような場合は、SkipLocalsInitAttribute を追加できます。In those cases, you can add the SkipLocalsInitAttribute. 1 つのメソッドまたはプロパティに、または classstructinterface に、さらにはモジュールに対してさえも、それを追加できます。You may add it to a single method or property, or to a class, struct, interface, or even a module. この属性は abstract メソッドに影響しません。実装用に生成されたコードに影響します。This attribute doesn't affect abstract methods; it affects the code generated for the implementation.

これらの機能により、一部のシナリオでパフォーマンスを向上させることができます。These features can improve performance in some scenarios. 導入前と導入後の両方で慎重にベンチマークを行った後でのみ、使用する必要があります。They should be used only after careful benchmarking both before and after adoption. ネイティブ サイズの整数に関するコードは、複数のターゲット プラットフォームで、異なる整数サイズを使用して、テストする必要があります。Code involving native sized integers must be tested on multiple target platforms with different integer sizes. その他の機能には、アンセーフ コードが必要です。The other features require unsafe code.

適合性と完成度の機能Fit and finish features

他の多くの機能は、コードをより効率的に記述するのに役立ちます。Many of the other features help you write code more efficiently. C# 9.0 では、作成されるオブジェクトの型が既にわかっている場合、newで型を省略できます。In C# 9.0, you can omit the type in a new expression when the created object's type is already known. 最も一般的な使用方法は、フィールドの宣言です。The most common use is in field declarations:

private List<WeatherObservation> _observations = new();

ターゲット型の new は、メソッドへの引数として渡す新しいオブジェクトを作成する必要がある場合にも使用できます。Target-typed new can also be used when you need to create a new object to pass as an argument to a method. 次のようなシグネチャを持つ ForecastFor() メソッドについて考えます。Consider a ForecastFor() method with the following signature:

public WeatherForecast ForecastFor(DateTime forecastDate, WeatherForecastOptions options)

これを、次のように呼び出すことができます。You could call it as follows:

var forecast = station.ForecastFor(DateTime.Now.AddDays(2), new());

この機能のもう 1 つの便利な用途は、init 専用プロパティと組み合わせて、新しいオブジェクトを初期化する場合です。Another nice use for this feature is to combine it with init only properties to initialize a new object:

WeatherStation station = new() { Location = "Seattle, WA" };

return new(); ステートメントを使用することで、既定のコンストラクターによって作成されたインスタンスを返すことができます。You can return an instance created by the default constructor using a return new(); statement.

同様の機能により、条件式の対象となる型の解決が向上します。A similar feature improves the target type resolution of conditional expressions. この変更により、2 つの式の間で暗黙的な変換を行う必要はありませんが、どちらもターゲット型への暗黙的な変換を行うことができます。With this change, the two expressions need not have an implicit conversion from one to the other, but may both have implicit conversions to a target type. 多くの場合、この変更に気付くことはありません。You likely won’t notice this change. 気付くとすれば、以前はキャストを必要としたり、コンパイルされなかったりした一部の条件式が、機能するようになることです。What you will notice is that some conditional expressions that previously required casts or wouldn’t compile now just work.

C# 9.0 以降では、static 修飾子をラムダ式または匿名メソッドに追加できます。Starting in C# 9.0, you can add the static modifier to lambda expressions or anonymous methods. 静的なラムダ式は、static ローカル関数に似ています。静的ラムダまたは匿名メソッドでは、ローカル変数またはインスタンスの状態をキャプチャできません。Static lambda expressions are analogous to the static local functions: a static lambda or anonymous method can't capture local variables or instance state. static 修飾子により、誤って他の変数がキャプチャされることがなくなります。The static modifier prevents accidentally capturing other variables.

共変の戻り値の型を使用すると、オーバーライドされた関数の戻り値の型を柔軟に指定できます。Covariant return types provide flexibility for the return types of overridden functions. オーバーライドされた仮想関数では、基底クラスのメソッドで宣言されている戻り値の型から派生した型を返すことができます。An overridden virtual function can return a type derived from the return type declared in the base class method. これは、レコードや、仮想クローンまたはファクトリ メソッドをサポートするその他の型に役立ちます。This can be useful for Records, and for other types that support virtual clone or factory methods.

また、foreach ループによって、それ以外の方法で foreach パターンを満たす拡張メソッド GetEnumerator が認識され、使用されます。In addition, the foreach loop will recognize and use an extension method GetEnumerator that otherwise satisfies the foreach pattern. この変更は、非同期パターンやパターンベースの分解など、他のパターンベースのコンストラクションと foreach の間に整合性があることを意味します。This change means foreach is consistent with other pattern-based constructions such as the async pattern, and pattern-based deconstruction. 実際、この変更は、あらゆる型に foreach サポートを追加できることを意味します。In practice, this change means you can add foreach support to any type. それの使用は、設計においてオブジェクトの列挙に意味があるときのみに限定してください。You should limit its use to when enumerating an object makes sense in your design.

次に、ラムダ式に対するパラメーターとして破棄を使用できます。Next, you can use discards as parameters to lambda expressions. このようにすると、引数の名前付けを避けることができ、コンパイラではその使用を避けることができます。This convenience enables you to avoid naming the argument, and the compiler may avoid using it. 任意の引数に対して _ を使用します。You use the _ for any argument. 詳細については、ラムダ式に関する記事の「ラムダ式の入力パラメーター」セクションを参照してください。For more information, see the Input parameters of a lambda expression section of the Lambda expressions article.

ようやく、ローカル関数に属性を適用できるようになりました。Finally, you can now apply attributes to local functions. たとえば、null 許容属性の注釈をローカル関数に適用できます。For example, you can apply nullable attribute annotations to local functions.

コード ジェネレーターのサポートSupport for code generators

2 つの最終機能では、C# コード ジェネレーターがサポートされています。Two final features support C# code generators. C# コード ジェネレーターは、roslyn アナライザーまたはコード修正と同じように記述できるコンポーネントです。C# code generators are a component you can write that is similar to a roslyn analyzer or code fix. 違いは、コード ジェネレーターでは、コードが分析され、コンパイル プロセスの一環として新しいソース コード ファイルが記述されることです。The difference is that code generators analyze code and write new source code files as part of the compilation process. 一般的なコード ジェネレーターでは、属性またはその他の規則がコードで検索されます。A typical code generator searches code for attributes or other conventions.

コード ジェネレーターでは、Roslyn 分析 API を使用して属性または他のコード要素が読み取られます。A code generator read attributes or other code elements using the Roslyn analysis APIs. その情報を基にして、コンパイルに新しいコードが追加されます。From that information, it adds new code to the compilation. ソース ジェネレーターではコードが追加されるだけで、コンパイル中の既存のコードの変更は許可されていません。Source generators can only add code; they aren't allowed to modify any existing code in the compilation.

コード ジェネレーターに対して追加された 2 つの機能は、"部分メソッド構文" の拡張機能と、"モジュール初期化子" です。The two features added for code generators are extensions to partial method syntax, and module initializers. 1 つ目は、部分メソッドに対する変更です。First, the changes to partial methods. C# 9.0 より前の部分メソッドは private ですが、アクセス修飾子を指定することはできず、戻り値は void で、out パラメーターを持つことはできません。Before C# 9.0, partial methods are private but can't specify an access modifier, have a void return, and can't have out parameters. これらの制限は、メソッドの実装を提供しないと、コンパイラによって部分メソッドのすべての呼び出しが削除されることを意味しました。These restrictions meant that if no method implementation is provided, the compiler removes all calls to the partial method. C# 9.0 ではこれらの制限はなくなりましたが、部分メソッドの宣言には実装が必要です。C# 9.0 removes these restrictions, but requires that partial method declarations have an implementation. コード ジェネレーターで、その実装を提供できます。Code generators can provide that implementation. 破壊的変更が発生しないよう、コンパイラでは、アクセス修飾子を持たないすべての部分メソッドは、古い規則に従うものと見なされます。To avoid introducing a breaking change, the compiler considers any partial method without an access modifier to follow the old rules. 部分メソッドに private アクセス修飾子が含まれている場合、その部分メソッドは新しい規則によって制御されます。If the partial method includes the private access modifier, the new rules govern that partial method.

コード ジェネレーターの 2 つ目の新機能は、"モジュール初期化子" です。The second new feature for code generators is module initializers. モジュール初期化子は、ModuleInitializerAttribute 属性が関連付けられているメソッドです。Module initializers are methods that have the ModuleInitializerAttribute attribute attached to them. これらのメソッドは、アセンブリが読み込まれるときに、ランタイムによって呼び出されます。These methods will be called by the runtime when the assembly loads. モジュール初期化子メソッドは次のようなものです。A module initializer method:

  • 静的でなければなりませんMust be static
  • パラメーターなしである必要がありますMust be parameterless
  • void を返す必要がありますMust return void
  • ジェネリック メソッドであってはなりませんMust not be a generic method
  • ジェネリック クラスに含まれていてはなりませんMust not be contained in a generic class
  • それを含むモジュールからアクセスできる必要がありますMust be accessible from the containing module

最後の項目は事実上、メソッドとそれを含んでいるクラスが internal または public である必要があることを意味します。That last bullet point effectively means the method and its containing class must be internal or public. メソッドをローカル関数にすることはできません。The method can't be a local function.