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

C# 7.0 では、C# 言語に多くの新機能が追加されます。C# 7.0 adds a number of new features to the C# language:

  • out 変数out variables
    • out の値は、それが使用されるメソッドの引数としてインラインで宣言できます。You can declare out values inline as arguments to the method where they're used.
  • タプルTuples
    • 複数のパブリック フィールドを含む、軽量で名前のない型を作成できます。You can create lightweight, unnamed types that contain multiple public fields. コンパイラおよび IDE ツールでは、このような型のセマンティクスが認識されます。Compilers and IDE tools understand the semantics of these types.
  • 破棄Discards
    • 破棄は、割り当てられた値を考慮しない場合に割り当てで使用された、一時的な書き込み専用の値です。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.
  • ref ローカル変数と戻り値ref locals and returns
    • メソッドのローカル変数と戻り値は、他のストレージへの参照になります。Method local variables and return values can be references to other storage.
  • ローカル関数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.
  • 一般化された async の戻り値の型Generalized async return types
    • async 修飾子を使って宣言したメソッドは、TaskTask<T> に加えて他の型を返すことができます。Methods declared with the async modifier can return other types in addition to Task and Task<T>.
  • 数値リテラルの構文の改善Numeric literal syntax improvements
    • 新しいトークンにより、数値定数の読みやすさが向上します。New tokens improve readability for numeric constants.

この記事の残りでは、各機能の概要について説明します。The remainder of this article provides an overview of each feature. 機能ごとに、その背後にある論拠のほか、For each feature, you'll learn the reasoning behind it. 構文についても説明します。You'll learn 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.

out 変数out variables

out パラメーターをサポートする既存の構文は、このバージョンで改良されました。The existing syntax that supports out parameters has been improved in this version. 現在は、別の宣言ステートメントを記述するのではなく、メソッド呼び出しの引数リストで 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(result);
else
    Console.WriteLine("Could not parse input");

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

if (int.TryParse(input, out var answer))
    Console.WriteLine(answer);
else
    Console.WriteLine("Could not parse input");
  • コードが読みやすくなる。The code is easier to read.
    • out 変数は、使用する場所で宣言します。その場所より上にある別の行で宣言しません。You declare the out variable where you use it, not on another line above.
  • 初期値を割り当てる必要がない。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.

タプルTuples

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# 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 runtime.

タプルの割り当てでは、代入の右辺でフィールドの名前を指定することもできます。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);
Console.WriteLine(max);
Console.WriteLine(min);

.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;

タプルの詳細については、タプルに関する記事を参照してください。You can learn more in depth about tuples in the tuples article.

破棄Discards

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 feature that allows you to implement method dispatch on properties other than the type of an object. オブジェクトの型に基づいたメソッドのディスパッチに慣れている方も多いはずです。You're probably already familiar with method dispatch based on the type of an object. オブジェクト指向プログラミングでは、仮想メソッドとオーバーライド メソッドには、オブジェクトの型に基づくメソッドのディスパッチを実装するための言語構文が用意されています。In object-oriented programming, virtual and override methods provide language syntax to implement method dispatching based on an object's type. 基底クラスと派生クラスは異なる実装を提供します。Base and Derived classes provide different implementations. パターン マッチング式により、この概念が拡張されたため、継承階層を介して関連していない型やデータ要素に同様のディスパッチ パターンを簡単に実装できるようになります。Pattern matching expressions extend this concept so that you can easily implement similar dispatch patterns for types and data elements that aren't related through an inheritance hierarchy.

パターン マッチングでは、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:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                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# のパターン マッチングを参照してください。You can learn more about pattern matching in Pattern Matching in C#.

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);
Console.WriteLine(item);
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 キーワードに関する記事をご覧ください。For more information, see the ref keyword article.

ローカル関数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.";
    }
}

注意

ローカル関数によってサポートされる設計の中には、"ラムダ式" を使用して実現できるものもあります。Some of the designs that are supported by local functions could 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.

一般化された 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 Framework に ValueTask 型が追加されました。As one concrete example, the ValueTask type has been added to the .NET framework 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.

数値リテラルの構文の改善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. バイナリ数値は非常に長くなる場合があるため、ビット パターンが見やすくなるように、桁区切り記号として _ が導入されました。上の画像のバイナリ定数で確認できます。Binary numbers can get long, so it's often easier to see the bit patterns by introducing the _ as a digit separator, as shown above in the binary constant. 桁区切り記号は定数のどこにでも置くことができます。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:

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.