C# 7.0 - C# 7.3 の新機能What's new in C# 7.0 through C# 7.3

C# 7.0 - C# 7.3 では、多くの機能が追加され、C# での開発エクスぺリエンスが段階的に改善されました。C# 7.0 through C# 7.3 brought a number of features and incremental improvements to your development experience with C#. この記事では、新しい言語機能とコンパイラ オプションの概要について説明します。This article provides an overview of the new language features and compiler options. .NET Framework ベースのアプリケーションでサポートされている最新バージョンである C# 7.3 の動作について説明します。The descriptions describe the behavior for C# 7.3, which is the most recent version supported for .NET Framework-based applications.

C# 7.1 で言語バージョンの選択の構成要素が追加され、これにより、プロジェクト ファイルでコンパイラ言語バージョンが指定できるようになりました。The language version selection configuration element was added with C# 7.1, which enables you to specify the compiler language version in your project file.

C# 7.0 - 7.3 で C# 言語に追加された機能とテーマは次のとおりです。C# 7.0-7.3 adds these features and themes to the C# language:

  • タプルと破棄Tuples and discards
    • 複数のパブリック フィールドを含む、軽量で名前のない型を作成できます。You can create lightweight, unnamed types that contain multiple public fields. コンパイラおよび IDE ツールでは、このような型のセマンティクスが認識されます。Compilers and IDE tools understand the semantics of these types.
    • 破棄は、割り当てられた値を考慮しない場合に割り当てで使用された、一時的な書き込み専用の値です。Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. タプルおよびユーザー定義の型を分解する場合や、メソッドを out パラメーターを使用して呼び出す場合に特に便利です。They're most useful when deconstructing tuples and user-defined types, as well as when calling methods with out parameters.
  • パターン一致Pattern Matching
    • これらの型のメンバーの任意の型と値に基づいて、分岐ロジックを作成できます。You can create branching logic based on arbitrary types and values of the members of those types.
  • async Mainメソッドasync Main method
    • アプリケーションのエントリ ポイントに async 修飾子を設定できます。The entry point for an application can have the async modifier.
  • ローカル関数Local Functions
    • 関数を他の関数の中に入れ子にして、関数のスコープと可視性を制限することができます。You can nest functions inside other functions to limit their scope and visibility.
  • 式形式のメンバーの追加More expression-bodied members
    • 式を使用して作成できるメンバーが増加しました。The list of members that can be authored using expressions has grown.
  • throwthrow Expressions
    • throw がステートメントだったためにこれまで許可されなかったコード コンストラクトで例外をスローできるようになりました。You can throw exceptions in code constructs that previously weren't allowed because throw was a statement.
  • default リテラル式default literal expressions
    • ターゲットの種類を推論できるとき、既定の値式で既定のリテラル式を使用できます。You can use default literal expressions in default value expressions when the target type can be inferred.
  • 数値リテラルの構文の改善Numeric literal syntax improvements
    • 新しいトークンにより、数値定数の読みやすさが向上します。New tokens improve readability for numeric constants.
  • out 変数out variables
    • out の値は、それが使用されるメソッドの引数としてインラインで宣言できます。You can declare out values inline as arguments to the method where they're used.
  • 末尾以外の名前付き引数Non-trailing named arguments
    • 名前付き引数の後ろに位置引数を続けることができます。Named arguments can be followed by positional arguments.
  • private protected アクセス修飾子private protected access modifier
    • private protected アクセス修飾子によって、同じアセンブリ内の派生クラスのアクセスが有効になります。The private protected access modifier enables access for derived classes in the same assembly.
  • オーバーロード解決の改善Improved overload resolution
    • オーバーロードの解決のあいまいさを解決するための新しい規則。New rules to resolve overload resolution ambiguity.
  • 安全で効率的なコードを記述するための手法Techniques for writing safe efficient code
    • 参照セマンティクスを使用したさまざまな値の型の使用を有効にする、構文の機能強化の組み合わせ。A combination of syntax improvements that enable working with value types using reference semantics.

最後に、コンパイラに新しいオプションが追加されました。Finally, the compiler has new options:

  • -refout-refonly: 参照アセンブリの生成を制御します。-refout and -refonly that control reference assembly generation.
  • -publicsign: オープン ソース ソフトウェア (OSS) のアセンブリの署名を可能にします。-publicsign to enable Open Source Software (OSS) signing of assemblies.
  • -pathmap: ソース ディレクトリのマッピングを提供します。-pathmap to provide a mapping for source directories.

この記事の残りでは、各機能の概要について説明します。The remainder of this article provides an overview of each feature. 各機能について、背後にある論拠と構文について説明します。For each feature, you'll learn the reasoning behind it and the syntax. dotnet try グローバル ツールを使って、これらの機能をご自身の環境で調べることができます。You can explore these features in your environment using the dotnet try global tool:

  1. dotnet try グローバル ツールをインストールします。Install the dotnet-try global tool.
  2. dotnet/try-samples リポジトリを複製します。Clone the dotnet/try-samples repository.
  3. 現在のディレクトリを、try-samples リポジトリの csharp7 サブディレクトリに設定します。Set the current directory to the csharp7 subdirectory for the try-samples repository.
  4. dotnet try を実行します。Run dotnet try.

タプルと破棄Tuples and discards

C# には、設計の意図を説明するために使用される、クラスと構造体の豊富な構文が用意されています。C# provides a rich syntax for classes and structs that is used to explain your design intent. ところが、場合によっては、その豊富な構文を使用するために、余分な作業が必要になることがあります。この場合、メリットはごくわずかです。But sometimes that rich syntax requires extra work with minimal benefit. 複数のデータ要素を含む単純な構造を必要とするメソッドを記述することはよくあります。You may often write methods that need a simple structure containing more than one data element. このようなシナリオをサポートするために、C# には "タプル" が追加されました。To support these scenarios tuples were added to C#. タプルとは、データ メンバーを表す複数のフィールドを含む軽量なデータ構造です。Tuples are lightweight data structures that contain multiple fields to represent the data members. フィールドは検証されず、独自のメソッドを定義することはできません。The fields aren't validated, and you can't define your own methods. C# のタプル型は、==!= をサポートしています。C# tuple types support == and !=. 詳しくは、For more information.


タプルは C# 7.0 より前で使用できましたが、効率的でなく、言語サポートがありませんでした。Tuples were available before C# 7.0, but they were inefficient and had no language support. これは、タプル要素が Item1Item2 などとしてのみ参照できることを意味しました。This meant that tuple elements could only be referenced as Item1, Item2 and so on. C# 7.0 では、タプルの言語サポートが導入されたことで、新しい、より効率的なタプル型を使用するフィールドのセマンティック名が有効になります。C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new, more efficient tuple types.

各メンバーに値を割り当て、任意でタプルの各メンバーにセマンティック名を付けることでタプルを作成できます。You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple:

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

namedLetters タプルには、AlphaBeta と呼ばれるフィールドが含まれています。The namedLetters tuple contains fields referred to as Alpha and Beta. これらの名前は、コンパイル時にのみ存在し、実行時にリフレクションを使用してタプルを検査するときなどには保持されません。Those names exist only at compile time and aren't preserved, for example when inspecting the tuple using reflection at run time.

タプルの割り当てでは、代入の右辺でフィールドの名前を指定することもできます。In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment:

var alphabetStart = (Alpha: "a", Beta: "b");
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

状況によっては、メソッドから返されたタプルのメンバーをばらすことが必要になる場合もあります。There may be times when you want to unpackage the members of a tuple that were returned from a method. そのためには、タプル内のそれぞれの値に対して別個の変数を宣言します。You can do that by declaring separate variables for each of the values in the tuple. このばらす行為は、タプルの分解と呼ばれます。This unpackaging is called deconstructing the tuple:

(int max, int min) = Range(numbers);

.NET でも任意の型に同様の分解を指定することができます。You can also provide a similar deconstruction for any type in .NET. クラスのメンバーとして Deconstruct メソッドを記述します。You write a Deconstruct method as a member of the class. その Deconstruct メソッドは、抽出する各プロパティ用に一連の out 引数を提供します。That Deconstruct method provides a set of out arguments for each of the properties you want to extract. 次の Point クラスを考えてみましょう。このクラスは、X 座標と Y 座標を抽出するデコンストラクター メソッドを指定しています。Consider this Point class that provides a deconstructor method that extracts the X and Y coordinates:

public class Point
    public Point(double x, double y)
        => (X, Y) = (x, y);

    public double X { get; }
    public double Y { get; }

    public void Deconstruct(out double x, out double y) =>
        (x, y) = (X, Y);

Point をタプルに割り当てて、個々のフィールドを抽出できます。You can extract the individual fields by assigning a Point to a tuple:

var p = new Point(3.14, 2.71);
(double X, double Y) = p;

タプルを何度初期化しても、代入の右項に使用される変数は、タプル要素に使用するものと同じ名前になります。タプル要素の名前は、タプルの初期化に使用される変数から推測できます。Many times when you initialize a tuple, the variables used for the right side of the assignment are the same as the names you'd like for the tuple elements: The names of tuple elements can be inferred from the variables used to initialize the tuple:

int count = 5;
string label = "Colors used in the map";
var pair = (count, label); // element names are "count" and "label"

この機能の詳細については、タプルの型に関する記事を参照してください。You can learn more about this feature in the Tuple types article.

out パラメーターを使用してタプルを分解したりメソッドを呼び出したりする場合に、使用する予定がなく、考慮にも入れない値の変数の定義を強制されることが多くあります。Often when deconstructing a tuple or calling a method with out parameters, you're forced to define a variable whose value you don't care about and don't intend to use. C# ではこのシナリオを処理するために破棄のサポートを追加しています。C# adds support for discards to handle this scenario. 破棄は名前が _ (アンダースコア (_) 文字) の書き込み専用の変数で、破棄するすべての値をこの 1 つの変数に割り当てることができます。A discard is a write-only variable whose name is _ (the underscore character); you can assign all of the values that you intend to discard to the single variable. 破棄は未割り当ての変数に似ています。代入ステートメントとは異なり、破棄はコードで使用できません。A discard is like an unassigned variable; apart from the assignment statement, the discard can't be used in code.

次のシナリオでは破棄はサポートされません。Discards are supported in the following scenarios:

  • タプルまたはユーザー定義の型を分解する場合。When deconstructing tuples or user-defined types.
  • out パラメーターを使用してメソッドを呼び出す場合。When calling methods with out parameters.
  • is および switch ステートメントによるパターン マッチング操作。In a pattern matching operation with the is and switch statements.
  • 割り当ての値を破棄として明示的に識別する必要がある場合の、スタンドアロン識別子。As a standalone identifier when you want to explicitly identify the value of an assignment as a discard.

次の例では、ある都市の 2 つの異なる年度のデータを含む 6 つのタプルを戻す QueryCityDataForYears メソッドを定義しています。The following example defines a QueryCityDataForYears method that returns a 6-tuple that contains data for a city for two different years. この例のメソッド呼び出しでは、メソッドによって戻された 2 つの人口の値のみが考慮されているため、タプルの残りの値はタプルの分解時に破棄として扱われます。The method call in the example is concerned only with the two population values returned by the method and so treats the remaining values in the tuple as discards when it deconstructs the tuple.

using System;
using System.Collections.Generic;

public class Example
    public static void Main()
        var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

        Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");

    private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
        int population1 = 0, population2 = 0;
        double area = 0;

        if (name == "New York City")
            area = 468.48;
            if (year1 == 1960)
                population1 = 7781984;
            if (year2 == 2010)
                population2 = 8175133;
            return (name, area, year1, population1, year2, population2);

        return ("", 0, 0, 0, 0, 0);
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

詳細については、破棄に関するページを参照してください。For more information, see Discards.

パターン マッチングPattern matching

"パターン マッチング" は、コード内の制御フローを新しい方法で表現できるようにする一連の機能です。Pattern matching is a set of features that enable new ways to express control flow in your code. 変数の型、値、またはプロパティの値をテストできます。You can test variables for their type, values or the values of their properties. これらの手法により、より読みやすいコード フローが作成されます。These techniques create more readable code flow.

パターン マッチングでは、is 式と switch 式がサポートされています。Pattern matching supports is expressions and switch expressions. どちらの式でも、オブジェクトとそのプロパティを検査して、そのオブジェクトが必要なパターンを満たしているかどうかを判定できます。Each enables inspecting an object and its properties to determine if that object satisfies the sought pattern. パターンに追加の規則を指定するには、when キーワードを使用します。You use the when keyword to specify additional rules to the pattern.

is パターン式を使用すると、使い慣れた is 演算子を拡張し、その型を超えてオブジェクトを照会したり、1 つの命令で結果を割り当てたりできます。The is pattern expression extends the familiar is operator to query an object about its type and assign the result in one instruction. 次のコードでは、変数が int であるかどうかが確認されます。int の場合、現在の合計に追加されます。The following code checks if a variable is an int, and if so, adds it to the current sum:

if (input is int count)
    sum += count;

先の小さな例では、is 式の拡張が示されています。The preceding small example demonstrates the enhancements to the is expression. 値の型や参照型に対してテストしたり、正しい型の新しい変数に成功した結果を割り当てたりできます。You can test against value types as well as reference types, and you can assign the successful result to a new variable of the correct type.

switch 一致式には、既に C# 言語に含まれている switch ステートメントに基づいた、使い慣れた構文があります。The switch match expression has a familiar syntax, based on the switch statement already part of the C# language. 更新された switch 式には新しいコンストラクトがいくつか含まれます。The updated switch statement has several new constructs:

  • switch 式を制御する型は、整数型、Enum 型、string、あるいはそれらの型のいずれかに対応する null 許容型に制限されなくなります。The governing type of a switch expression is no longer restricted to integral types, Enum types, string, or a nullable type corresponding to one of those types. 任意の型を使用できます。Any type may be used.
  • case ラベルで switch 式の型をテストできます。You can test the type of the switch expression in each case label. is 式と同様に、その型に新しい変数を割り当てることができます。As with the is expression, you may assign a new variable to that type.
  • when 句を追加し、その変数で条件をさらにテストできます。You may add a when clause to further test conditions on that variable.
  • case ラベルの順序が重要になります。The order of case labels is now important. 一致する最初の分岐が実行されます。他の分岐はスキップされます。The first branch to match is executed; others are skipped.

次のコードでこれらの新しい機能を確認できます。The following code demonstrates these new features:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
    int sum = 0;
    foreach (var i in sequence)
        switch (i)
            case 0:
            case IEnumerable<int> childSequence:
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
            case int n when n > 0:
                sum += n;
            case null:
                throw new NullReferenceException("Null found in sequence");
                throw new InvalidOperationException("Unrecognized type");
    return sum;
  • case 0: はおなじみの定数パターンです。case 0: is the familiar constant pattern.
  • case IEnumerable<int> childSequence: は型パターンです。case IEnumerable<int> childSequence: is a type pattern.
  • case int n when n > 0:when 条件が追加された型パターンです。case int n when n > 0: is a type pattern with an additional when condition.
  • case null: は null パターンです。case null: is the null pattern.
  • default: はおなじみの既定ケースです。default: is the familiar default case.

C#7.1 以降では、is 型パターンと switch 型パターンのパターン式は、ジェネリック型パラメーターの型を持つことができます。Beginning with C# 7.1, the pattern expression for is and the switch type pattern may have the type of a generic type parameter. これは struct 型か class 型のいずれかである可能性がある型を確認し、ボックス化を回避するときに最も役立ちます。This can be most useful when checking types that may be either struct or class types, and you want to avoid boxing.

パターン マッチングの詳細については、C# のパターン マッチングを参照してください。You can learn more about pattern matching in Pattern Matching in C#.

async mainAsync main

async main メソッドにより、Main メソッドで await を使用できます。An async main method enables you to use await in your Main method. 以前は次のように記述する必要がありました。Previously you would need to write:

static int Main()
    return DoAsyncWork().GetAwaiter().GetResult();

それが次のように記述できるようになりました。You can now write:

static async Task<int> Main()
    // This could also be replaced with the body
    // DoAsyncWork, including its await expressions:
    return await DoAsyncWork();

プログラムによって終了コードが返されない場合、Task を返す Main メソッドを宣言できます。If your program doesn't return an exit code, you can declare a Main method that returns a Task:

static async Task Main()
    await SomeAsyncMethod();

プログラミング ガイドの async main の記事に詳細があります。You can read more about the details in the async main article in the programming guide.

ローカル関数Local functions

クラスの多くの設計には、1 つの場所からのみ呼び出されるメソッドが含まれます。Many designs for classes include methods that are called from only one location. このような追加のプライベート メソッドを使用することで、各メソッドのサイズを小さくし、その焦点を絞ることができます。These additional private methods keep each method small and focused. ローカル関数を使用すると、別のメソッドのコンテキスト内でメソッドを宣言することができます。Local functions enable you to declare methods inside the context of another method. ローカル関数のおかげで、クラスを読み取る際に、ローカル メソッドはそれ自体が宣言されているコンテキストからしか呼び出されないことが、簡単にわかります。Local functions make it easier for readers of the class to see that the local method is only called from the context in which it is declared.

ローカル関数には、パブリック反復子メソッドとパブリック非同期メソッドという 2 つの一般的なユース ケースがあります。There are two common use cases for local functions: public iterator methods and public async methods. どちらの種類のメソッドも、プログラマーが期待するよりも遅くエラーを報告するコードを生成します。Both types of methods generate code that reports errors later than programmers might expect. 反復子メソッドの場合、例外が検出されるのは、返されたシーケンスを列挙するコードを呼び出した場合のみです。In iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. 非同期メソッドの場合、例外が検出されるのは、返された Task が待機状態になったときのみです。In async methods, any exceptions are only observed when the returned Task is awaited. 次の例では、ローカル関数を使用し、反復子の実装からパラメーター検証を分ける動作を確認できます。The following example demonstrates separating parameter validation from the iterator implementation using a local function:

public static IEnumerable<char> AlphabetSubset3(char start, char end)
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation()
        for (var c = start; c < end; c++)
            yield return c;

同じ手法を async メソッドで使用すると、引数の検証で発生する例外が非同期操作の開始前にスローされることを保証できます。The same technique can be employed with async methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins:

public Task<string> PerformLongRunningWork(string address, int index, string name)
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";

次の構文がサポートされるようになりました。This syntax is now supported:

[field: SomeThingAboutFieldAttribute]
public int SomeProperty { get; set; }

属性 SomeThingAboutFieldAttribute は、SomeProperty のコンパイラによって生成されるバッキング フィールドに適用されます。The attribute SomeThingAboutFieldAttribute is applied to the compiler generated backing field for SomeProperty. 詳しくは、C# プログラミング ガイドの「属性」を参照してください。For more information, see attributes in the C# programming guide.


ローカル関数によってサポートされる設計の中には、"ラムダ式" を使用して実現できるものもあります。Some of the designs that are supported by local functions can also be accomplished using lambda expressions. 詳細については、ローカル関数とラムダ式に関するページをご覧ください。For more information, see Local functions vs. lambda expressions.

式形式のメンバーの追加More expression-bodied members

C# 6 では、メンバー関数の式形式のメンバーと読み取り専用プロパティが導入されました。C# 6 introduced expression-bodied members for member functions, and read-only properties. C# 7.0 では、式として実装できる許可されたメンバーが拡張されます。C# 7.0 expands the allowed members that can be implemented as expressions. C# 7.0 では、"コンストラクター"、"ファイナライザー"、get アクセサー、および set アクセサーを "プロパティ" と "インデクサー" に実装できます。In C# 7.0, you can implement constructors, finalizers, and get and set accessors on properties and indexers. それぞれの例を次のコードに示します。The following code shows examples of each:

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// Expression-bodied get / set accessors.
public string Label
    get => label;
    set => this.label = value ?? "Default label";


この例ではファイナライザーは必要ありませんが、その構文を紹介するために示しています。This example does not need a finalizer, but it is shown to demonstrate the syntax. アンマネージ リソースを解放する必要がない限り、クラスにファイナライザーを実装しないでください。You should not implement a finalizer in your class unless it is necessary to release unmanaged resources. また、アンマネージ リソースを直接管理する代わりに、SafeHandle クラスの使用を検討する必要もあります。You should also consider using the SafeHandle class instead of managing unmanaged resources directly.

式形式のメンバー用のこれらの新しい場所は、C# 言語の重要なマイルストーンです。これらの機能は、オープン ソースの Roslyn プロジェクトに関わるコミュニティ メンバーによって実装されました。These new locations for expression-bodied members represent an important milestone for the C# language: These features were implemented by community members working on the open-source Roslyn project.

メソッドを式のようなメンバーに変更することは、バイナリ互換性がある変更です。Changing a method to an expression bodied member is a binary compatible change.

throw 式Throw expressions

C# では、throw は常にステートメントでした。In C#, throw has always been a statement. throw は式ではなくステートメントであるため、使用できない C# コンストラクトがありました。Because throw is a statement, not an expression, there were C# constructs where you couldn't use it. これには、条件式、null 結合式、および一部のラムダ式が含まれます。These included conditional expressions, null coalescing expressions, and some lambda expressions. 式形式のメンバーが追加されたことにより、さらに多くの場所で throw 式が役に立つようになりました。The addition of expression-bodied members adds more locations where throw expressions would be useful. C# 7.0 では、このようなコンストラクトを記述できるように、throw 式が導入されています。So that you can write any of these constructs, C# 7.0 introduces throw expressions.

この導入により、式ベースのコードをたくさん記述することが簡単になります。This addition makes it easier to write more expression-based code. エラー チェックのためにステートメントを追加する必要はありません。You don't need additional statements for error checking.

既定のリテラル式Default literal expressions

既定のリテラル式は既定の値式の拡張版です。Default literal expressions are an enhancement to default value expressions. これらの式によって変数が初期化され、既定値になります。These expressions initialize a variable to the default value. 以前は次のように記述していました。Where you previously would write:

Func<string, bool> whereClause = default(Func<string, bool>);

それが今では、初期化の右項で種類を省略できるようになりました。You can now omit the type on the right-hand side of the initialization:

Func<string, bool> whereClause = default;

詳しくは、「default 演算子」記事の「default リテラル」セクションをご覧ください。For more information, see the default literal section of the default operator article.

数値リテラルの構文の改善Numeric literal syntax improvements

数値定数を読み間違えると、コードを初めて読むときにコードを理解するのが難しくなる場合があります。Misreading numeric constants can make it harder to understand code when reading it for the first time. ビット マスクやその他の記号を用いた値では誤解を招きやすくなります。Bit masks or other symbolic values are prone to misunderstanding. C# 7.0 では、使用目的に応じて最も読みやすい形式で数値を記述しできるように、バイナリ リテラル桁区切り文字という 2 つの新機能が導入されました。C# 7.0 includes two new features to write numbers in the most readable fashion for the intended use: binary literals, and digit separators.

ビット マスクを作成しているときや、数値をバイナリで表現するとコードが最も読みやすくなる場合は、数字をバイナリで記述します。For those times when you're creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary:

public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

定数の先頭にある 0b は、数値が 2 進数として記述されていることを示します。The 0b at the beginning of the constant indicates that the number is written as a binary number. 2 進数は長くなる可能性があるため、前の例の 2 進定数に示されているように、_ を桁区切り文字として導入すると、ビット パターンが見やすくなることがよくあります。Binary numbers can get long, so it's often easier to see the bit patterns by introducing the _ as a digit separator, as shown in the binary constant in the preceding example. 桁区切り記号は定数のどこにでも置くことができます。The digit separator can appear anywhere in the constant. 10 進数の場合は、3 桁の区切り記号として使用するのが一般的です。For base 10 numbers, it is common to use it as a thousands separator. 16 進と 2 進の数値リテラルの先頭に _ を使用できます。Hex and binary numeric literals may begin with an _:

public const long BillionsAndBillions = 100_000_000_000;

桁区切り記号は、decimal 型、float 型、double 型でも使用できます。The digit separator can be used with decimal, float, and double types as well:

public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

これらをまとめると、数値定数をさらに見やすい状態で宣言できます。Taken together, you can declare numeric constants with much more readability.

out 変数out variables

out パラメーターをサポートする既存の構文は、C# 7 で改良されました。The existing syntax that supports out parameters has been improved in C# 7. 現在は、別の宣言ステートメントを記述するのではなく、メソッド呼び出しの引数リストで out 変数を宣言できるようになりました。You can now declare out variables in the argument list of a method call, rather than writing a separate declaration statement:

if (int.TryParse(input, out int result))
    Console.WriteLine("Could not parse input");

前の例に示されているように、わかりやすくするために out 変数の型を指定することができます。You may want to specify the type of the out variable for clarity, as shown in the preceding example. ただし、この言語では、次のように暗黙的に型指定されたローカル変数を使用できます。However, the language does support using an implicitly typed local variable:

if (int.TryParse(input, out var answer))
    Console.WriteLine("Could not parse input");
  • コードが読みやすくなる。The code is easier to read.
    • out 変数は、前のコード行ではなく、使用する場所で宣言します。You declare the out variable where you use it, not on a preceding line of code.
  • 初期値を割り当てる必要がない。No need to assign an initial value.
    • out 変数は、メソッド呼び出し内の使用場所で宣言することにより、割り当てる前に誤って使用することがなくなります。By declaring the out variable where it's used in a method call, you can't accidentally use it before it is assigned.

out 変数の宣言を許可するために C# 7.0 に追加された構文が、フィールド初期化子、プロパティ初期化子、コンストラクター初期化子、およびクエリ句を含めるように拡張されました。The syntax added in C# 7.0 to allow out variable declarations has been extended to include field initializers, property initializers, constructor initializers, and query clauses. これにより、次の例に示すようなコードを使用できるようになります。It enables code such as the following example:

public class B
   public B(int i, out int j)
      j = i;

public class D : B
   public D(int i) : base(i, out var j)
      Console.WriteLine($"The value of 'j' is {j}");

末尾以外の名前付き引数Non-trailing named arguments

メソッド呼び出しで、位置引数の前に名前付き引数を使用できるようになりました。ただし、そのような名前付き引数が正しい位置にある場合です。Method calls may now use named arguments that precede positional arguments when those named arguments are in the correct positions. 詳細については、「名前付き引数と省略可能な引数」を参照してください。For more information, see Named and optional arguments.

private protected アクセス修飾子private protected access modifier

新しい複合アクセス修飾子: private protected は、同じアセンブリで宣言されているクラスまたは派生クラスを含むことでメンバーにアクセスできることを示しています。A new compound access modifier: private protected indicates that a member may be accessed by containing class or derived classes that are declared in the same assembly. protected internal は同じアセンブリの派生クラスまたはクラスによるアクセスを許可していますが、private protected は同じアセンブリで宣言された派生型へのアクセスを制限しています。While protected internal allows access by derived classes or classes that are in the same assembly, private protected limits access to derived types declared in the same assembly.

詳細については、言語リファレンスのアクセス修飾子に関するページを参照してください。For more information, see access modifiers in the language reference.

オーバーロード候補の改善Improved overload candidates

すべてのリリースにおいて、あいまいなメソッド呼び出しに対する "明らかな" 選択肢が存在する状況に対応するようにオーバーロードの解決ルールが更新されました。In every release, the overload resolution rules get updated to address situations where ambiguous method invocations have an "obvious" choice. このリリースでは、コンパイラが明らかな選択肢を選択できるようにする 3 つの新しいルールが追加されています。This release adds three new rules to help the compiler pick the obvious choice:

  1. インスタンス メンバーと静的メンバーの両方がメソッド グループに含まれている場合は、インスタンス レシーバーまたはコンテキストを指定せずにメソッドが呼び出されると、コンパイラがインスタンス メンバーを破棄します。When a method group contains both instance and static members, the compiler discards the instance members if the method was invoked without an instance receiver or context. インスタンス レシーバーを使用してメソッドが呼び出された場合、コンパイラは静的メンバーを破棄します。The compiler discards the static members if the method was invoked with an instance receiver. レシーバーがない場合、コンパイラは静的メンバーだけを静的コンテキストに含めます。それ以外の場合は、静的メンバーとインスタンス メンバーの両方を含めます。When there is no receiver, the compiler includes only static members in a static context, otherwise both static and instance members. レシーバーがあいまいなインスタンスまたは型である場合、コンパイラは両方のメンバーを含めます。When the receiver is ambiguously an instance or type, the compiler includes both. 暗黙的な this インスタンス レシーバーを使用できない静的コンテキストには、this が定義されないメンバー (静的メンバーなど) の本体および this を使用できない場所 (フィールド初期化子やコンストラクター初期化子など) が含まれます。A static context, where an implicit this instance receiver cannot be used, includes the body of members where no this is defined, such as static members, as well as places where this cannot be used, such as field initializers and constructor-initializers.
  2. 型引数が制約を満たしていない複数のジェネリック メソッドがメソッド グループに含まれている場合、そのグループのメンバーは候補セットから削除されます。When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
  3. メソッド グループの変換では、戻り値の型がデリゲートの戻り値の型と一致しない候補メソッドが候補セットから削除されます。For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set.

適切なメソッドがわかっている場合には、あいまいなメソッドのオーバーロードに対するコンパイラ エラーが少なくなることを認識できるため、この変更点にのみ注意してください。You'll only notice this change because you'll find fewer compiler errors for ambiguous method overloads when you are sure which method is better.

セーフ コードをより効率的にするEnabling more efficient safe code

アンセーフ コードと同様のパフォーマンスを確保した C# コードを安全に記述できるようにする必要があります。You should be able to write C# code safely that performs as well as unsafe code. セーフ コードは、バッファー オーバーラン、ストレイ ポインター、その他のメモリ アクセス エラーなどのエラーを回避します。Safe code avoids classes of errors, such as buffer overruns, stray pointers, and other memory access errors. ここで説明する新機能は、検証可能なセーフ コードの機能を拡張します。These new features expand the capabilities of verifiable safe code. 安全なコンストラクトを使用してより多くのコードを記述するようにしてください。Strive to write more of your code using safe constructs. 以下に示す機能によって、コードの記述が容易になります。These features make that easier.

次の新機能は、セーフ コードのパフォーマンス向上のテーマをサポートします。The following new features support the theme of better performance for safe code:

  • ピン留めを使用せずに fixed フィールドにアクセスできます。You can access fixed fields without pinning.
  • ref ローカル変数を再割り当てできます。You can reassign ref local variables.
  • stackalloc 配列で初期化子を使用できます。You can use initializers on stackalloc arrays.
  • パターンをサポートする型と共に fixed ステートメントを使用できます。You can use fixed statements with any type that supports a pattern.
  • 追加のジェネリック制約を使用できます。You can use additional generic constraints.
  • パラメーターの in 修飾子。引数が参照によって渡されるが、呼び出されたメソッドでは変更されないことを指定します。The in modifier on parameters, to specify that an argument is passed by reference but not modified by the called method. 引数に in 修飾子を加えることは、ソース互換性がある変更です。Adding the in modifier to an argument is a source compatible change.
  • メソッド戻りの ref readonly 修飾子。メソッドが参照によってその値を戻しますが、そのオブジェクトに対する書き込みを許可しないことを指定します。The ref readonly modifier on method returns, to indicate that a method returns its value by reference but doesn't allow writes to that object. 戻り値が値に割り当てられている場合、ref readonly 修飾子を追加することは、ソース互換性がある変更です。Adding the ref readonly modifier is a source compatible change, if the return is assigned to a value. 既存の ref return ステートメントに readonly 修飾子を追加することは、互換性がない変更です。Adding the readonly modifier to an existing ref return statement is an incompatible change. 呼び出し元は、readonly 修飾子を含むように ref ローカル変数の宣言を更新する必要があります。It requires callers to update the declaration of ref local variables to include the readonly modifier.
  • readonly struct 宣言。変更不可の構造体で、そのメンバー メソッドの in パラメーターとして渡す必要があることを示します。The readonly struct declaration, to indicate that a struct is immutable and should be passed as an in parameter to its member methods. 既存の構造体の宣言に readonly 修飾子を追加することは、バイナリ互換性がある変更です。Adding the readonly modifier to an existing struct declaration is a binary compatible change.
  • ref struct 宣言。構造体型がマネージド メモリに直接アクセスし、常にスタック割り当てが必要であることを示します。The ref struct declaration, to indicate that a struct type accesses managed memory directly and must always be stack allocated. 既存の struct の宣言に ref 修飾子を追加することは、互換性がない変更です。Adding the ref modifier to an existing struct declaration is an incompatible change. ref struct をクラスのメンバーにすることはできません。また、ヒープ上に割り当てられている可能性がある他の場所で使用することもできません。A ref struct cannot be a member of a class or used in other locations where it may be allocated on the heap.

これらすべての変更点の詳細については、安全で効率的なコードを記述する方法に関するページを参照してください。You can read more about all these changes in Write safe efficient code.

ref ローカル変数と戻り値Ref locals and returns

この機能により、他の場所に定義されている変数への参照を使用したり返したりするアルゴリズムが実現します。This feature enables algorithms that use and return references to variables defined elsewhere. 1 つの例として、大規模なマトリックスを使用していて、特定の特性を持つ 1 つの場所を探します。One example is working with large matrices, and finding a single location with certain characteristics. 次のメソッドでは、マトリックスのそのストレージに参照が返されます。The following method returns a reference to that storage in the matrix:

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");

戻り値を ref として宣言し、次のコードのように、マトリックスでその値を変更できます。You can declare the return value as a ref and modify that value in the matrix, as shown in the following code:

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
item = 24;
Console.WriteLine(matrix[4, 2]);

C# 言語には、ref ローカル変数と戻り値の誤用を防ぐ規則がいくつかあります。The C# language has several rules that protect you from misusing the ref locals and returns:

  • メソッド シグネチャと、メソッド内のすべての return ステートメントに ref キーワードを追加する必要があります。You must add the ref keyword to the method signature and to all return statements in a method.
    • それにより、メソッド全体でメソッドは参照渡しで返すことになります。That makes it clear the method returns by reference throughout the method.
  • ref return は値の変数か ref 変数に割り当てることができます。A ref return may be assigned to a value variable, or a ref variable.
    • 呼び出し元により、戻り値がコピーされるかどうかが制御されます。The caller controls whether the return value is copied or not. 戻り値を割り当てるとき、ref 修飾子を省略すると、呼び出し元はストレージの参照ではなく、値のコピーを求めることになります。Omitting the ref modifier when assigning the return value indicates that the caller wants a copy of the value, not a reference to the storage.
  • 標準的なメソッドの戻り値を ref ローカル変数に割り当てることはできません。You can't assign a standard method return value to a ref local variable.
    • したがって、ref int i = sequence.Count(); のようなステートメントは使用できません。That disallows statements like ref int i = sequence.Count();
  • 有効期間がメソッドの実行期間を超えない変数に ref を返すことはできません。You can't return a ref to a variable whose lifetime doesn't extend beyond the execution of the method.
    • つまり、ローカル変数または類似のスコープの変数への参照を返すことはできません。That means you can't return a reference to a local variable or a variable with a similar scope.
  • ref ローカル変数と戻り値は、非同期メソッドと共に使用することはできません。ref locals and returns can't be used with async methods.
    • コンパイラは、非同期メソッドが戻るときに、参照先の変数が、最終的な値に設定されているかどうかを認識できません。The compiler can't know if the referenced variable has been set to its final value when the async method returns.

ref ローカル変数および ref 戻り値の追加により、値のコピーを回避したり、逆参照操作を複数回実行したりすることで、より効率的なアルゴリズムを実現できます。The addition of ref locals and ref returns enables algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times.

戻り値に ref を追加することは、ソース互換性がある変更です。Adding ref to the return value is a source compatible change. 既存のコードはコンパイルされますが、参照戻り値は割り当て時にコピーされます。Existing code compiles, but the ref return value is copied when assigned. 呼び出し元は、戻り値を参照として格納するために、戻り値の記憶域を ref ローカル変数に更新する必要があります。Callers must update the storage for the return value to a ref local variable to store the return as a reference.

初期化後に別のインスタンスを参照するために、ref ローカル変数を再割り当てできるようになりました。Now, ref locals may be reassigned to refer to different instances after being initialized. 次のコードがコンパイルされます。The following code now compiles:

ref VeryLargeStruct refLocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.

詳しくは、ref 戻り値と ref ローカル変数に関する記事と foreach に関する記事を参照してください。For more information, see the article on ref returns and ref locals, and the article on foreach.

詳しくは、ref キーワードに関する記事をご覧ください。For more information, see the ref keyword article.

条件付きの refConditional ref expressions

最後に、条件式で値の結果ではなく参照結果を生成することができます。Finally, the conditional expression may produce a ref result instead of a value result. たとえば、次のように記述して、2 つの配列のいずれかに含まれる最初の要素の参照を取得できますFor example, you would write the following to retrieve a reference to the first element in one of two arrays:

ref var r = ref (arr != null ? ref arr[0] : ref otherArr[0]);

変数 r は、arr または otherArr の最初の値の参照です。The variable r is a reference to the first value in either arr or otherArr.

詳細については、言語リファレンスの条件演算子 (?:) に関するページを参照してください。For more information, see conditional operator (?:) in the language reference.

in パラメーター修飾子in parameter modifier

in キーワードは、ref と out の既存のキーワードを補完し、引数を参照で渡します。The in keyword complements the existing ref and out keywords to pass arguments by reference. in キーワードでは、引数を参照で渡すことが指定されますが、呼び出されたメソッドでは値は変更されません。The in keyword specifies passing the argument by reference, but the called method doesn't modify the value.

次のコードに示すように、値または読み取り専用参照によって渡されるオーバーロードを宣言できます。You may declare overloads that pass by value or by readonly reference, as shown in the following code:

static void M(S arg);
static void M(in S arg);

値によるオーバーロード (前述の例の 1 番目) の方が、読み取り専用参照による方法よりも優れています。The by value (first in the preceding example) overload is better than the by readonly reference version. readonly 参照引数を使用してバージョンを呼び出すには、メソッドの呼び出し時に in 修飾子を含める必要があります。To call the version with the readonly reference argument, you must include the in modifier when calling the method.

詳しくは、in パラメーター修飾子に関する記事を参照してください。For more information, see the article on the in parameter modifier.

fixed ステートメントをサポートする型の増加More types support the fixed statement

fixed ステートメントは、限られた一連の型をサポートしていました。The fixed statement supported a limited set of types. C# 7.3 以降では、ref T または ref readonly T を返す GetPinnableReference() メソッドを格納する型として fixed を使用できます。Starting with C# 7.3, any type that contains a GetPinnableReference() method that returns a ref T or ref readonly T may be fixed. この機能の追加により、fixedSystem.Span<T> および関連する型と共に使用できます。Adding this feature means that fixed can be used with System.Span<T> and related types.

詳しくは、言語リファレンスの fixed ステートメントに関する記事を参照してください。For more information, see the fixed statement article in the language reference.

ピン留めが不要な fixed フィールドのインデックス付けIndexing fixed fields does not require pinning

たとえば、次の構造体があるとします。Consider this struct:

unsafe struct S
    public fixed int myFixedField[10];

以前のバージョンの C# では、myFixedField の一部であるいずれかの整数にアクセスするために変数のピン留めが必要でした。In earlier versions of C#, you needed to pin a variable to access one of the integers that are part of myFixedField. 今では、次のコードは、変数 p を別の fixed ステートメントの内部にピン留めせずに、コンパイルされます。Now, the following code compiles without pinning the variable p inside a separate fixed statement:

class C
    static S s = new S();

    unsafe public void M()
        int p = s.myFixedField[5];

変数 pmyFixedField の 1 個の要素にアクセスします。The variable p accesses one element in myFixedField. 個別の int* 変数を宣言する必要はありません。You don't need to declare a separate int* variable. unsafe コンテキストは引き続き必要です。You still need an unsafe context. 以前のバージョンの C# では、2 番目の固定ポインターを宣言する必要があります。In earlier versions of C#, you need to declare a second fixed pointer:

class C
    static S s = new S();

    unsafe public void M()
        fixed (int* ptr = s.myFixedField)
            int p = ptr[5];

詳細については、fixed ステートメントに関する記事を参照してください。For more information, see the article on the fixed statement.

stackalloc 配列による初期化子のサポートstackalloc arrays support initializers

配列を初期化する際に、配列内の要素の値を指定することが可能でした。You've been able to specify the values for elements in an array when you initialize it:

var arr = new int[3] {1, 2, 3};
var arr2 = new int[] {1, 2, 3};

現在では、stackalloc で宣言された配列に同じ構文を適用できます。Now, that same syntax can be applied to arrays that are declared with stackalloc:

int* pArr = stackalloc int[3] {1, 2, 3};
int* pArr2 = stackalloc int[] {1, 2, 3};
Span<int> arr = stackalloc [] {1, 2, 3};

詳細については、「stackalloc 演算子」の記事を参照してください。For more information, see the stackalloc operator article.

ジェネリック制約の拡張Enhanced generic constraints

型パラメーターの基底クラスの制約として、System.Enum 型または System.Delegate 型を指定できるようになりました。You can now specify the type System.Enum or System.Delegate as base class constraints for a type parameter.

また、新しい unmanaged 制約を使用して、型パラメーターが null 非許容でアンマネージ型である必要があることを指定することもできます。You can also use the new unmanaged constraint, to specify that a type parameter must be a non-nullable unmanaged type.

詳しくは、where ジェネリック制約および型パラメーターの制約に関する記事を参照してください。For more information, see the articles on where generic constraints and constraints on type parameters.

これらの制約を既存の型に追加することは、互換性のない変更です。Adding these constraints to existing types is an incompatible change. クローズ ジェネリック型は、これらの新しい制約を満たさなくなります。Closed generic types may no longer meet these new constraints.

一般化された async の戻り値の型Generalized async return types

非同期メソッドから Task オブジェクトを返すと、特定のパスでパフォーマンスのボトルネックが発生する可能性があります。Returning a Task object from async methods can introduce performance bottlenecks in certain paths. Task は参照型です。したがって、これを使うことは、オブジェクトを割り当てることを意味します。Task is a reference type, so using it means allocating an object. async 修飾子で宣言されたメソッドがキャッシュされた結果を返すか、同期的に完了する場合、追加の割り当ては、コードのパフォーマンスが重要なセクションにおいて大きな時間コストにつながります。In cases where a method declared with the async modifier returns a cached result, or completes synchronously, the extra allocations can become a significant time cost in performance critical sections of code. 厳密なループ処理でこのような割り当てが発生した場合、コストがかかる場合があります。It can become costly if those allocations occur in tight loops.

新しい言語機能では、非同期メソッドの戻り値の型が TaskTask<T>void に限定されません。The new language feature means that async method return types aren't limited to Task, Task<T>, and void. 返される型は引き続き非同期パターンを満たす必要があります。つまり、GetAwaiter メソッドはアクセス可能である必要があります。The returned type must still satisfy the async pattern, meaning a GetAwaiter method must be accessible. 1 つの具体的な例として、この新しい言語機能を使用するために .NET に ValueTask 型が追加されました。As one concrete example, the ValueTask type has been added to .NET to make use of this new language feature:

public async ValueTask<int> Func()
    await Task.Delay(100);
    return 5;


ValueTask<TResult> 型を使用するには、NuGet パッケージ System.Threading.Tasks.Extensions > を追加する必要があります。You need to add the NuGet package System.Threading.Tasks.Extensions > in order to use the ValueTask<TResult> type.

この機能強化は、ライブラリの作成時、パフォーマンス クリティカルなコードに Task を割り当てることを回避する目的で非常に便利です。This enhancement is most useful for library authors to avoid allocating a Task in performance critical code.

新しいコンパイラ オプションNew compiler options

新しいコンパイラ オプションでは、C# プログラムの新しいビルドと DevOps のシナリオがサポートされます。New compiler options support new build and DevOps scenarios for C# programs.

参照アセンブリ生成Reference assembly generation

"参照専用アセンブリ" を生成する新しい 2 つのコンパイラ オプション -refout-refonly があります。There are two new compiler options that generate reference-only assemblies: -refout and -refonly. リンク先の記事には、オプションと参照アセンブリに関する詳細があります。The linked articles explain these options and reference assemblies in more detail.

公開署名またはオープン ソース署名Public or Open Source signing

-publicsign コンパイラ オプションは、公開キーを使用してアセンブリに署名するようにコンパイラに指示します。The -publicsign compiler option instructs the compiler to sign the assembly using a public key. アセンブリは署名済みとしてマークされますが、署名は公開キーから取得されます。The assembly is marked as signed, but the signature is taken from the public key. このオプションでは、公開キーを使用するオープン ソース プロジェクトから署名済みのアセンブリをビルドできます。This option enables you to build signed assemblies from open-source projects using a public key.

詳しくは、-publicsign コンパイラ オプションの記事を参照してください。For more information, see the -publicsign compiler option article.


-pathmap コンパイラ オプションは、ビルド環境からのソース パスをマップ済みのソース パスに置き換えるようにコンパイラに指示します。The -pathmap compiler option instructs the compiler to replace source paths from the build environment with mapped source paths. -pathmap オプションは、コンパイラが記述した PDB ファイルまたは CallerFilePathAttribute のソース パスを制御します。The -pathmap option controls the source path written by the compiler to PDB files or for the CallerFilePathAttribute.

詳しくは、-pathmap コンパイラ オプションの記事を参照してください。For more information, see the -pathmap compiler option article.