C# 6 の新機能What's New in C# 6

C# の 6.0 リリースには、開発者の生産性を向上させる多くの機能が含まれています。The 6.0 release of C# contained many features that improve productivity for developers. これらの機能がもたらす全体的な効果として、より読みやすく簡潔なコードを記述できるようになりました。The overall effect of these features is that you write more concise code that is also more readable. 一般的なベスト プラクティスを多数反映することで、構文に使用される形式的な記述が減っています。The syntax contains less ceremony for many common practices. 形式的な記述が減ったことで、設計の意図が理解しやすくなっています。It's easier to see the design intent with less ceremony. これらの機能を十分に学習すると、生産性が向上し、より読みやすいコードを記述できるようになります。Learn these features well, and you'll be more productive and write more readable code. 言語のコンストラクトよりも機能により集中することができます。You can concentrate more on your features than on the constructs of the language.

この記事の残りの部分では、これらの各機能の概要と、各機能をより詳しく学習するためのリンクを提供します。The rest of this article provides an overview of each of these features, with a link to explore each feature. チュートリアル セクションの C# 6 の対話型探索で、機能を探究することもできます。You can also explore the features in an interactive exploration on C# 6 in the tutorials section.

読み取り専用の自動プロパティRead-only auto-properties

読み取り専用の自動プロパティを使用すると、より簡潔な構文で不変型を作成できます。Read-only auto-properties provide a more concise syntax to create immutable types. 自動プロパティは、get アクセサーのみを使用して宣言します。You declare the auto-property with only a get accessor:

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

FirstName プロパティと LastName プロパティは、同じクラスのコンストラクターの本体内でのみ設定できます。The FirstName and LastName properties can be set only in the body of the constructor of the same class:

public Student(string firstName, string lastName)
{
    if (IsNullOrWhiteSpace(lastName))
        throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
    FirstName = firstName;
    LastName = lastName;
}

別のメソッドで LastName を設定しようとすると、コンパイル エラー CS0200 が生成されます。Trying to set LastName in another method generates a CS0200 compilation error:

public class Student
{
    public string LastName { get;  }

    public void ChangeName(string newLastName)
    {
        // Generates CS0200: Property or indexer cannot be assigned to -- it is read only
        LastName = newLastName;
    }
}

この機能を使用すれば、不変型を作成したり、より簡潔で便利な自動プロパティ構文を使用するための、本格的な言語サポートを実現できます。This feature enables true language support for creating immutable types and uses the more concise and convenient auto-property syntax.

この構文を追加してもアクセス可能なメソッドが削除されない場合は、バイナリ互換性がある変更です。If adding this syntax doesn't remove an accessible method, it's a binary compatible change.

自動プロパティ初期化子Auto-property initializers

自動プロパティ初期化子を使用すると、プロパティ宣言の一部として自動プロパティの初期値を宣言できます。Auto-property initializers let you declare the initial value for an auto-property as part of the property declaration.

public ICollection<double> Grades { get; } = new List<double>();

Grades メンバーは宣言された場所で初期化されます。The Grades member is initialized where it's declared. これにより、初期化を厳密に 1 回だけ実行しやすくなります。That makes it easier to perform the initialization exactly once. 初期化がプロパティ宣言の一部になることで、ストレージ割り当てが Student オブジェクトのパブリック インターフェイスと同じであることを示しやすくなっています。The initialization is part of the property declaration, making it easier to equate the storage allocation with the public interface for Student objects.

式形式の関数メンバーExpression-bodied function members

記述する多くのメンバーは、単一の式の可能性がある 1 つのステートメントです。Many members that you write are single statements that could be single expressions. 代わりに式形式のメンバーを記述します。Write an expression-bodied member instead. この方法は、メソッドと読み取り専用プロパティに有効です。It works for methods and read-only properties. たとえば、ToString() のオーバーライドはその好例です。For example, an override of ToString() is often a great candidate:

public override string ToString() => $"{LastName}, {FirstName}";

読み取り専用プロパティには、次の構文を使用することもできます。You can also use this syntax for read-only properties:

public string FullName => $"{FirstName} {LastName}";

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

using staticusing static

using static 拡張機能を使用すると、1 つのクラスの静的メソッドをインポートすることができます。The using static enhancement enables you to import the static methods of a single class. 具体的には、使用するクラスを次のように指定します。You specify the class you're using:

using static System.Math;

Math にはいずれのインスタンス メソッドも含まれていません。The Math does not contain any instance methods. また using static は、静的メソッドとインスタンス メソッドの両方を持つクラスの静的クラスをインポートするためにも使用できます。You can also use using static to import a class' static methods for a class that has both static and instance methods. 特に便利な例の 1 つが String です。One of the most useful examples is String:

using static System.String;

注意

static using ステートメントでは、完全修飾クラス名 (System.String) 使用する必要があります。You must use the fully qualified class name, System.String in a static using statement. 代わりに string キーワードを使用することはできません。You cannot use the string keyword instead.

static using ステートメントからインポートすると、拡張メソッドは、拡張メソッドの呼び出し構文を使用して呼び出された場合にのみ、スコープ内に含められます。When imported from a static using statement, extension methods are only in scope when called using the extension method invocation syntax. 静的メソッドとして呼び出された場合には含められません。They aren't in scope when called as a static method. これは、LINQ クエリで多く見受けられることになるでしょう。You'll often see this in LINQ queries. LINQ パターンは、Enumerable または Queryable をインポートすることによってインポートできます。You can import the LINQ pattern by importing Enumerable, or Queryable.

using static System.Linq.Enumerable;

拡張メソッドは通常、拡張メソッドの呼び出し式を使用して呼び出します。You typically call extension methods using extension method invocation expressions. まれなケースですが、静的メソッドの呼び出し構文を使用して拡張メソッドを呼び出す場合は、クラス名を追加することであいまいさを解決します。Adding the class name in the rare case where you call them using static method call syntax resolves ambiguity.

static using ディレクティブは、入れ子にされた型もインポートします。The static using directive also imports any nested types. これにより、入れ子にされた型を修飾なしで参照できます。You can reference any nested types without qualification.

Null 条件演算子Null-conditional operators

null 条件演算子を使用すれば、これらの null チェックをより簡単で円滑なものにすることができます。The null conditional operator makes null checks much easier and fluid. メンバー アクセス .?. に置き換えます。Replace the member access . with ?.:

var first = person?.FirstName;

上記の例では、person オブジェクトが null の場合に、変数 firstnull が割り当てられます。In the preceding example, the variable first is assigned null if the person object is null. それ以外の場合には、FirstName プロパティの値が割り当てられます。Otherwise, it is assigned the value of the FirstName property. 特に重要なのは、?. を使用した場合、変数 personnull の場合、このコード行では NullReferenceException が生成されないということです。Most importantly, the ?. means that this line of code doesn't generate a NullReferenceException if the person variable is null. 代わりに、処理がショートサーキットされ、null が返されます。Instead, it short-circuits and returns null. null 条件演算子は配列アクセスまたはインデクサー アクセスにも使用できます。You can also use a null conditional operator for array or indexer access. インデックス式の []?[] に置き換えます。Replace [] with ?[] in the index expression.

次の式では、person の値に関係なく、string が返されます。The following expression returns a string, regardless of the value of person. このコンストラクトは、いずれかのプロパティが null の場合に既定値を割り当てる目的で、null 結合演算子と共によく使用されます。You often use this construct with the null coalescing operator to assign default values when one of the properties is null. 式がショート サーキットされると、返された null 値が式全体に一致するように入力されます。When the expression short-circuits, the null value returned is typed to match the full expression.

first = person?.FirstName ?? "Unspecified";

?. を使用してメソッドを条件付きで呼び出すこともできます。You can also use ?. to conditionally invoke methods. null 条件演算子を使用したメンバー関数の最も一般的な用途は、null の可能性があるデリゲート (またはイベント ハンドラー) を安全に呼び出すことです。The most common use of member functions with the null conditional operator is to safely invoke delegates (or event handlers) that may be null. ?. 演算子を使用してデリゲートの Invoke メソッドを呼び出して、メンバーにアクセスします。You'll call the delegate's Invoke method using the ?. operator to access the member. 例については、デリゲート パターンに関する記事でご覧になれます。You can see an example in the delegate patterns article.

?. 演算子のルールでは、、演算子の左側が 1 回だけ評価されることが保証されています。The rules of the ?. operator ensure that the left-hand side of the operator is evaluated only once. これによって、イベント ハンドラーを使用した次の例のように、多くの表現方法が可能になります。It enables many idioms, including the following example using event handlers:

// preferred in C# 6:
this.SomethingHappened?.Invoke(this, eventArgs);

左辺が 1 回だけ評価されることを保証し、また ?. の左辺で、メソッドの呼び出しなどの任意の式を使用できるようにします。Ensuring that the left side is evaluated only once also enables you to use any expression, including method calls, on the left side of the ?.

文字列補間String interpolation

C# 6 では、新しい文字列補間機能を使用し、文字列に式を埋め込むことができます。With C# 6, the new string interpolation feature enables you to embed expressions in a string. 文字列の前に $ を付けて、序数の代わりに {} の間に式を使用するだけです。Simply preface the string with $and use expressions between { and } instead of ordinals:

public string FullName => $"{FirstName} {LastName}";

この例では、置換される式にプロパティを使用しています。This example uses properties for the substituted expressions. 任意の式を使用できます。You can use any expression. たとえば、補間の一部として生徒の評価点の平均を計算することもできます。For example, you could compute a student's grade point average as part of the interpolation:

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

上記のコード行では、Grades.Average() の値が、小数点以下 2 桁の浮動小数点数として書式設定されます。The preceding line of code formats the value for Grades.Average() as a floating-point number with two decimal places.

しかし場合によっては、生成された文字列を特定のカルチャで書式設定する必要が生じる場合もあるでしょう。Often, you may need to format the string produced using a specific culture. 文字列補間によって生成されたオブジェクトは暗黙的に System.FormattableString に変換できることを利用します。You use the fact that the object produced by a string interpolation can be implicitly converted to System.FormattableString. FormattableString インスタンスには、複合書式文字列と、それらを文字列に変換する前の式評価の結果が含まれます。The FormattableString instance contains the composite format string and the results of evaluating the expressions before converting them to strings. FormattableString.ToString(IFormatProvider) メソッドを使用し、文字列の書式設定時にカルチャを指定します。Use the FormattableString.ToString(IFormatProvider) method to specify the culture when formatting a string. 次の例は、ドイツ (de-DE) のカルチャを使用して文字列を生成しますThe following example produces a string using the German (de-DE) culture. (既定では、ドイツのカルチャでは小数点区切り文字に ',' 文字が使用され、桁区切り記号に '.' 文字が使用されます)。(By default, the German culture uses the ',' character for the decimal separator, and the '.' character as the thousands separator.)

FormattableString str = $"Average grade is {s.Grades.Average()}";
var gradeStr = str.ToString(new System.Globalization.CultureInfo("de-DE"));

文字列補間を開始するには、C# における文字列補間の対話型チュートリアル、文字列補間に関する記事、および「C# における文字列補間」のチュートリアルを参照してください。To get started with string interpolation, see the String interpolation in C# interactive tutorial, the String interpolation article, and the String interpolation in C# tutorial.

例外フィルターException filters

例外フィルターは、特定の catch 句がいつ適用されるのかを決定する句です。Exception Filters are clauses that determine when a given catch clause should be applied. 例外フィルターに使用されている式が true と評価された場合、catch 句は例外に対して通常の処理を実行します。If the expression used for an exception filter evaluates to true, the catch clause performs its normal processing on an exception. 式が false と評価された場合、catch 句はスキップされます。If the expression evaluates to false, then the catch clause is skipped. 用途としては、例外に関する情報を調べて、catch 句で例外を処理できるかどうかを確認するという使い方があります。One use is to examine information about an exception to determine if a catch clause can process the exception:

public static async Task<string> MakeRequest()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
        var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
        try
        {
            var responseText = await stringTask;
            return responseText;
        }
        catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
    }
}

nameofThe nameof expression

nameof 式では、シンボルの名前が評価されます。The nameof expression evaluates to the name of a symbol. この式は、変数、プロパティ、またはメンバー フィールドの名前が必要なときに便利です。It's a great way to get tools working whenever you need the name of a variable, a property, or a member field. nameof の用途として特に一般的なものの 1 つは、例外の原因となったシンボルの名前を取得することです。One of the most common uses for nameof is to provide the name of a symbol that caused an exception:

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

また、INotifyPropertyChanged インターフェイスを実装する XAML ベースのアプリケーションでも使用されます。Another use is with XAML-based applications that implement the INotifyPropertyChanged interface:

public string LastName
{
    get { return lastName; }
    set
    {
        if (value != lastName)
        {
            lastName = value;
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(nameof(LastName)));
        }
    }
}
private string lastName;

Catch ブロックと Finally ブロックでの AwaitAwait in Catch and Finally blocks

C# 5 では、await 式を配置できる位置について、いくつかの制限がありました。C# 5 had several limitations around where you could place await expressions. C# 6 では、awaitcatch 式や finally 式で使用できるようになりました。With C# 6, you can now use await in catch or finally expressions. 次に示すのは、ロギングのシナリオで特によく使用されるコードです。This is most often used with logging scenarios:

public static async Task<string> MakeRequestAndLogFailures()
{
    await logMethodEntrance();
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        await logError("Recovered from redirect", e);
        return "Site Moved";
    }
    finally
    {
        await logMethodExit();
        client.Dispose();
    }
}

catch 句と finally 句の内部に await のサポートを追加するための実装詳細では、その動作と、同期コードの動作との整合性が保証されています。The implementation details for adding await support inside catch and finally clauses ensure that the behavior is consistent with the behavior for synchronous code. catch または finally 句で実行されたコードがエラーをスローした場合、プログラムは次の包含ブロック内から適切な catch 句を検索します。When code executed in a catch or finally clause throws, execution looks for a suitable catch clause in the next surrounding block. 現行の例外があった場合、その例外は失われます。If there was a current exception, that exception is lost. catch 句や finally 句で待機中の式についても、これと同じことが起こります。つまり、適切な catch が検索され、現行の例外がある場合は、その例外が失われます。The same happens with awaited expressions in catch and finally clauses: a suitable catch is searched for, and the current exception, if any, is lost.

注意

この動作を理由に、catch 句と finally 句は慎重に記述することが推奨されています (新しい例外が導入されないようにしてください)。This behavior is the reason it's recommended to write catch and finally clauses carefully, to avoid introducing new exceptions.

インデクサーを使用して関連コレクションを初期化するInitialize associative collections using indexers

インデックス初期化子は、インデックスの使用によってコレクション初期化子の一貫性を高める 2 つの機能のうちの 1 つです。Index Initializers is one of two features that make collection initializers more consistent with index usage. C# の以前のリリースでは、キーと値のペアを中かっこで囲むことで Dictionary<TKey,TValue> を含めたシーケンス スタイルのコレクションでコレクション初期化子を使用できました。In earlier releases of C#, you could use collection initializers with sequence style collections, including Dictionary<TKey,TValue>, by adding braces around key and value pairs:

private Dictionary<int, string> messages = new Dictionary<int, string>
{
    { 404, "Page not Found"},
    { 302, "Page moved, but left a forwarding address."},
    { 500, "The web server can't come out to play today."}
};

それらを Dictionary<TKey,TValue> コレクションおよびアクセス可能な Add メソッドが複数の引数を受け取るその他の型と共に使用できます。You can use them with Dictionary<TKey,TValue> collections and other types where the accessible Add method accepts more than one argument. 新しい構文は、インデックスを使用したコレクションへの割り当てをサポートしています。The new syntax supports assignment using an index into the collection:

private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

この機能が加わったことにより、いくつかのバージョンでシーケンス コンテナーに使用されていたものと同様の構文を使用して、連想コンテナーを初期化できるようになりました。This feature means that associative containers can be initialized using syntax similar to what's been in place for sequence containers for several versions.

コレクション初期化子内の拡張 Add メソッドExtension Add methods in collection initializers

コレクション初期化を使いやすくするもう 1 つの機能として、 メソッドの拡張メソッドAddを使用できるようになりました。Another feature that makes collection initialization easier is the ability to use an extension method for the Add method. この機能は、Visual Basic との同等性を確保するために追加されました。This feature was added for parity with Visual Basic. この機能は、新しい項目を意味的に追加するために、カスタム コレクション クラスで別の名前のメソッドを使用している場合に特に便利です。The feature is most useful when you have a custom collection class that has a method with a different name to semantically add new items.

オーバーロード解決の改善Improved overload resolution

最後に説明するのは、言われなければ気付かないかもしれない機能です。This last feature is one you probably won't notice. 以前のバージョンの C# コンパイラでは、ラムダ式を使用した一部のメソッド呼び出しがあいまいと判断されるコンストラクトがありました。There were constructs where the previous version of the C# compiler may have found some method calls involving lambda expressions ambiguous. たとえば、次のメソッドがあったとします。Consider this method:

static Task DoThings()
{
     return Task.FromResult(0);
}

以前のバージョンの C# では、メソッド グループ構文を使用してこのメソッドを呼び出すと、エラーが発生していました。In earlier versions of C#, calling that method using the method group syntax would fail:

Task.Run(DoThings);

以前のコンパイラでは、Task.Run(Action)Task.Run(Func<Task>()) を正しく区別することができませんでした。The earlier compiler couldn't distinguish correctly between Task.Run(Action) and Task.Run(Func<Task>()). 以前のバージョンでは、引数としてラムダ式を使用する必要がありました。In previous versions, you'd need to use a lambda expression as an argument:

Task.Run(() => DoThings());

C# 6 の コンパイラは、Task.Run(Func<Task>()) のほうが適切であるということを正しく判断できます。The C# 6 compiler correctly determines that Task.Run(Func<Task>()) is a better choice.

決定論的コンパイラの出力Deterministic compiler output

-deterministic オプションが、同じソース ファイルの連続するコンパイルで、バイト単位で同じ出力アセンブリを生成するようにコンパイラに指示します。The -deterministic option instructs the compiler to produce a byte-for-byte identical output assembly for successive compilations of the same source files.

既定では、すべてのコンパイルでは、コンパイルごとに一意な出力が生成されます。By default, every compilation produces unique output on each compilation. コンパイラは、タイムスタンプを追加し、ランダムな数から生成された GUID を追加します。The compiler adds a timestamp, and a GUID generated from random numbers. ビルド間の一貫性を確保するために、バイト単位で出力を比較する場合は、このオプションを使用します。You use this option if you want to compare the byte-for-byte output to ensure consistency across builds.

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