rksoftware

Visual Studio とか C# とかが好きです

C# 2.0 以降の新機能まとめ(概要とコード)

 C# 2.0 以降の新機能の名前と公式ガイドページへのリンクをまとめました。

 注1)機能の名前はできるだけ公式ガイドから言葉を拾うようにしましたが、完全ではありません。
 注2)リンク先ページはできるだけ日本語ページを拾うようにしましたが、見つけられずに英語のページもあります。
 注3)良さそうなページを探しましたが、もっと良いページがあるかもしれません。

■ イベント

 この記事は次のイベントの準備としてまとめています。

■ 関連リンク

・C# 2.0 以降の新機能まとめ(名前とリンク)

■ コードの試し方

 全てコンソールアプリケーションで実行できるようにしています。また、コードの記述もコンソールアプリケーションを新規作成した際に生成される Program.cs ファイルだけで実行できます。
 メソッドの定義等がないコードの場合は、Program クラスの Main メソッド内へ貼り付けてください。

■ C# 2.0 での新機能

・ジェネリック
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/generics/
 List<T> クラスなどの T の機能。List<int> とすると int 型しか格納できないタイプセーフな List を使用できる。

List<int> ints = new List<int>();
// ints.Add("text"); <- これはできない
ints.Add(1);
int i = ints[0];
Console.WriteLine(i);

List<string> strings = new List<string>();
// strings.Add(1); <- これはできない
strings.Add("text");
string s = strings[0];
Console.WriteLine(s);

Console.ReadKey();

・匿名メソッド
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/statements-expressions-operators/anonymous-methods
 [推奨されません(※)] delegate をインライン コードで生成できる。 (※)C# 3.0 で追加されたラムダ式を使用してください。

System.Threading.Timer t = new System.Threading.Timer(
    delegate (object state) { Console.WriteLine("delegate"); } // 匿名メソッド
    , null, 0, 1000
    );
// "delegate" を繰り返し出力

Console.ReadKey();

・反復子 (yield)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/iterators
 反復子を使用して、リストや配列などのコレクションをステップ実行することができる。

static void Main(string[] args)
{
    foreach (int number in Values())
    {
        Console.WriteLine(number.ToString());    // 1
                                                 // 2
                                                 // 3
    }

    Console.ReadKey();
}

public static IEnumerable<int> Values()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

・部分型定義 (Partial クラス/構造体)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/partial-type
 クラス、構造体、またはインターフェイスの定義を複数のファイルに分割することができる。

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        a.A1(); // A1
        a.A2(); // A2

        Console.ReadKey();
    }
}

partial class A
{
    public void A1() { Console.WriteLine("A1"); }
}

partial class A
{
    public void A2() { Console.WriteLine("A2"); }
}

・Null 許容型 (Nullable)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/nullable-types/
 Null 許容型は、基になる値型の値だけでなく、null 値も表すことができる。

int? a = 1;
a = null;
Console.WriteLine(a == null);   // True
a = 1;
Console.WriteLine(a.Value);     // 1

Console.ReadKey();

・?? 演算子
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/null-conditional-operator
 null 合体演算子と呼ばれる。左側のオペランドが null 値でない場合には左側のオペランドを返し、null 値である場合には右側のオペランドを返す。

string a = "a";
string b = null;
Console.WriteLine(a);   // a
Console.WriteLine(b ?? "b is null");    // b is null

Console.ReadKey();

・プロパティの get/set 個別のアクセスレベル
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/restricting-accessor-accessibility
 プロパティの get アクセサーと set で異なるアクセス レベルを設定できる。

class Program
{
    static void Main(string[] args)
    {
        A a = new A();
        // a.PropertyA = 1; <- これはできない
        a.SetPropertyA(10);
        int propertyValue = a.PropertyA;
        Console.WriteLine(propertyValue);   // 10

        Console.ReadKey();
    }
}

class A
{
    public int PropertyA { get; private set; }
    public void SetPropertyA(int value) { PropertyA = value; }
}

・static クラス
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members
 new キーワードを使用して、そのクラス型の変数を作成できないクラスを定義できる。

class Program
{
    static void Main(string[] args)
    {
        // A a = new A(); <- これはできない
        A.PropertyA = 1;
        int a = A.PropertyA;
        Console.WriteLine(a);   // 1

        Console.ReadKey();
    }
}

static class A
{
    public static int PropertyA { get; set; }
}

・:: 演算子
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/namespace-alias-qualifer
  名前空間エイリアス修飾子として global を指定すると、常にグローバル名前空間で検索が実行される。

class Program
{
    static void Main(string[] args)
    {
        System.Console.WriteLine("csharp");         // MyMethod
        global::System.Console.WriteLine("csharp"); // csharp

        global::System.Console.ReadKey();
    }
}

class System
{
    internal class Console
    {
        internal static void WriteLine(string text) {
            global::System.Console.WriteLine("MyMethod");
        }
    }
}

・extern エイリアス
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/extern-alias
 同じ完全修飾型名を持つ、2 つアセンブリを参照する場合に名前空間のエイリアスを設定できる。
 [コード例なし] 同じ完全修飾型名を持つアセンブリを用意し参照する際の機能で、コードだけでは示せない。

・#pragma プリプロセッサディレクティブ
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/preprocessor-directives/preprocessor-pragma
 特殊な命令をコンパイラに指示できる。

#pragma warning disable CS0168
int i;  // CS0168 の警告(変数未使用)がでない
#pragma warning restore CS0168
int j;  // CS0168 の警告(変数未使用)がでる

Console.ReadKey();

・Conditional 属性
 https://msdn.microsoft.com/ja-jp/library/aa664622(v=vs.71).aspx
 条件付きコンパイル シンボルによる条件付きメソッドを定義ができる。シンボルが定義されていない場合、メソッドの呼び出しが行われない。

static void Main(string[] args)
{
    WriteDebug();   // デバッグ時のみ実行される
    Console.WriteLine("END");

    Console.ReadKey();
}

[System.Diagnostics.Conditional("DEBUG")]
public static void WriteDebug()
{
    Console.WriteLine("Debug Mode");
}

・固定サイズ バッファー (fixed)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/unsafe-code-pointers/fixed-size-buffers
 DLL や COM を扱う際に使える、固定サイズの配列を持ったバッファーを作成することができる。

public fixed char fixedBuffer[128];

・デリゲートの分散 (共変性と反変性)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/covariance-contravariance/using-variance-in-delegates
 共変性により、メソッドの戻り値の型をデリゲートに定義されている型のサブクラスにできる。 反変性によりメソッドのパラメーター型をデリゲートのパラメーターの型のスーパークラスにできる。

class Program
{
    class A { }
    class A2 : A { }
    class A3 : A { }

    delegate A2 MyDelegate(A2 a2);

    static void Main(string[] args)
    {
        MyDelegate deleg1 = Method1;    // <- これはできる
        A a = deleg1(new A2());
        //MyDelegate deleg2 = Method2;    <- これはできない
        //MyDelegate deleg3 = Method3;    < -これはできない
        Console.ReadKey();
    }

    static A2 Method1(A a)
    {
        return null;
    }

    static A2 Method2(A3 a)
    {
        return null;
    }

    static A Method3(A2 a)
    {
        return null;
    }
}

■ C# 3.0 での新機能

・型推論 (var)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/var
 変数の宣言時に暗黙の型指定を行える。コンパイラが型を推論してくれる。

var i = 10; // i は int 型の変数
i = 20;     // <- これはできる
// i = "text"; // これはできない

Console.WriteLine(i.GetType().Name);

・配列の型推論 (暗黙的に型指定される配列)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/arrays/implicitly-typed-arrays
 配列の宣言時に暗黙の型指定を行える。コンパイラが型を推論してくれる。

var ints = new[] { 0, 20, 30 }; // ints は int 型の配列
ints[0] = 10;     // <- これはできる
// ints[0] = "text"; <- これはできない
foreach(var i in ints)
{
    Console.WriteLine(i); // 10
                          // 20
                          // 30
}

Console.ReadKey();

・自動実装プロパティ
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties
 値の取得または設定時にロジックが必要ない場合、プロパティを簡潔に宣言できる。

class Program
{
    static void Main(string[] args)
    {
        Phone p = new Phone();
        p.Name = "窓スマ";
        p.Size = 5.0;
        Console.WriteLine(string.Format("{0}, {1} インチ",p.Name, p.Size));    // 窓スマ, 5 インチ

        Console.ReadKey();
    }
}

class Phone
{
    internal string Name { get; set; }
    internal double Size { get; set; }
}

・拡張メソッド
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
 既存の型に外からメソッドを追加できる。

class Program
{
    static void Main(string[] args)
    {
        string s = "text";
        Console.WriteLine(s.HasValue());    // True
        s = null;
        Console.WriteLine(s.HasValue());    // False

        Console.ReadKey();
    }
}

static class StringExtensions
{
    internal static bool HasValue(this string value)
    {
        return !string.IsNullOrEmpty(value);
    }
}

・ラムダ式
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions
 匿名関数。引数として渡したり関数呼び出しの結果値として返すことができるローカル関数を記述できる。あらゆる場所で使われるが、LINQ を使用する際に特に重要。

System.Threading.Timer t = new System.Threading.Timer(
    // delegate (object state) { Console.WriteLine("delegate"); } // delegate による匿名メソッド
    state => Console.WriteLine("lambda") // ラムダ式による匿名メソッド
    // (object state) => { Console.WriteLine("lambda"); } // <- このような記述も可能
    ,null, 0, 1000
);
// "lambda" を繰り返し出力

Console.ReadKey();

・LINQ
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/linq/query-syntax-and-method-syntax-in-linq
 データソースからデータを取得する。その際、変換や集計、グループ化などを行うことも可能。クエリ式とメソッド式という 2 つの形式があるが、一般にメソッド式が使われる。下記はメソッド式の例。

int[] data = new[] { 1, 2, 3, 4, 5, 6 };

var evens = data.Where(x => x % 2 == 0);        // 偶数だけ取り出す
foreach (var i in evens)
{
    Console.Write(i);  // 246
}

Console.WriteLine();

var ws = data.Select(x => x * 2);               // 全てに 2 を乗算する
foreach (var i in ws)
{
    Console.Write(i);     // 24681012
}

Console.WriteLine();

var evensws = data.Where(x => x % 2 == 0).Select(x => x * 2);   // 偶数だけ取り出し 2 を乗算する
foreach (var i in evensws) {
    Console.Write(i);     // 4812
}

Console.WriteLine();

Console.WriteLine(data.Max());  // 6;
Console.WriteLine(data.Min());  // 1;
Console.WriteLine(data.Sum());  // 21

Console.ReadKey();

・匿名型
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types
 明示的に型を定義することなく、複数の読み取り専用プロパティを持ったオブジェクトを生成できる。 各プロパティの型はコンパイラにより推論される。

var p = new { Name = "窓スマ", Size = 5.0 };

Console.WriteLine(string.Format("{0}, {1} インチ", p.Name, p.Size));    // 窓スマ, 5 インチ
Console.WriteLine(string.Format("p is {0}", p.GetType().Name));    // p is <>f__AnonymousType0`2
Console.WriteLine(string.Format("Name is {0}", p.Name.GetType().Name));    // Name is String
Console.WriteLine(string.Format("Size is {0}", p.Size.GetType().Name));    // Name is Double

Console.ReadKey();

・オブジェクト初期化子
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers
 オブジェクトの作成時にプロパティに、値を割り当てることができる。匿名型の使用時にも使われる。

class Program
{
    static void Main(string[] args)
    {
        MyPoint p = new MyPoint() { X = 10, Y = 20 };
        Console.WriteLine(string.Format("{0}, {1}", p.X, p.Y)); // 10, 20

        var anonymous = new[] { 1, 2, 3 }.Select(x => new { Source = x, W = x * 2 });
        foreach(var m in anonymous)
        {
            Console.WriteLine(string.Format("{0}, {1}", m.Source, m.W)); // 1, 2
                                                                         // 2, 4
                                                                         // 3, 6
        }

        Console.ReadKey();
    }
}

class MyPoint
{
    internal int X { get; set; }
    internal int Y { get; set; }
}

・Partial メソッド
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/partial-method
 Partial クラスの定義時にメソッドのシグネチャと実装を分けられる。定義できるメソッドは void を返す private メソッドのみ。

class Program
{
    static void Main(string[] args)
    {
        new A().Method();           // 何も出力されない
        Console.WriteLine("====="); // =====
        new B().Method();           // Class B Method

        Console.ReadKey();
    }
}

partial class A
{
    internal void Method()
    {
        this.PartialMethod("Class A Method");
    }

    partial void PartialMethod(string text);
}

partial class A
{
    //partial void PartialMethod(string text)
    //{
    //    Console.WriteLine(text);
    //}
}

partial class B
{
    internal void Method()
    {
        this.PartialMethod("Class B Method");
    }

    partial void PartialMethod(string text);
}

partial class B
{
    partial void PartialMethod(string text)
    {
        Console.WriteLine(text);
    }
}

■ C# 4.0 での新機能

・dynamic 型
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/dynamic
 dynamic 型はコンパイル時の型チェックを行わず、実行時に解決される。

try
{
    dynamic a = 1;
    Console.WriteLine(a);                   // 1
    Console.WriteLine(a.GetType().Name);    // Int32

    a = a + 1;
    Console.WriteLine(a);                   // 2

    a = "text";
    a = a + 1;
    Console.WriteLine(a);                   // text1
    Console.WriteLine(a.GetType().Name);    // String
    Console.WriteLine(a.ToString());        // text1

    Console.WriteLine(a.ToString2());       // <- 実行時エラー「'string' に 'ToString2' の定義がありません」
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Console.ReadKey();

・省略可能な引数 (オプション引数)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments
 メソッドなどのパラメーターに規定値を設定し、省略可能にできる。

static void Main(string[] args)
{
    AddAndWriteLine(10, 20, "計算結果 => {0}"); // 計算結果 => 30
    AddAndWriteLine(10, 20);                   // 結果は 30 です.
    AddAndWriteLine(1);                        // 結果は 3 です.

    Console.ReadKey();
}

static int AddAndWriteLine(int a, int b=2, string format="結果は {0} です.")
{
    var c = a + b;
    Console.WriteLine(string.Format(format, c));
    return c;
}

・名前付き引数
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments
 メソッドなどを呼び出す際、パラメーターを名前付きで記述できる。パラメーターを任意の順に指定できる。

static void Main(string[] args)
{
    AddAndWriteLine(10, 20, "計算結果 => {0}");             // 計算結果 => 30
    // AddAndWriteLine(10, "計算結果 => {0}", 20);          // <- これはできない
    AddAndWriteLine(10, format:"計算結果 => {0}", b:20);    // <- パラメーターを任意の順序で指定できる
    AddAndWriteLine(10, format: "計算結果 => {0}");         // <- 途中のパラメーターを省略できる
    AddAndWriteLine(10, 20, format:"計算結果 => {0}");      // 引数の意味が分かりやすくなる

    Console.ReadKey();
}

static int AddAndWriteLine(int a, int b=2, string format="結果は {0} です.")
{
    var c = a + b;
    Console.WriteLine(string.Format(format, c));
    return c;
}

・ジェネリックの共変性と反変性
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/in-generic-modifier
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/out-generic-modifier
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces
 共変性により、ジェネリック型の変数に型パラメータがサブクラスにであるオブジェクトを代入できる。 反変性によりジェネリック型の変数に型パラメータがスーパークラスにであるオブジェクトを代入できる。

class Program
{
    static void Main(string[] args)
    {
        // 反変 実体のメソッド Set(object) に、Set(string) で string をパラメーターとしても問題ない
        IA<string> a = new A<object>();
        a.Set("text");

        // 共変 実体の戻り値 string を、object 変数に代入しても問題ない
        IB<object> b = new B<string>();
        object o = b.Get();

        Console.ReadKey();
    }
}

interface IA<in T>
{
    void Set(T t);
}

class A<T> : IA<T>
{
    public void Set(T t) { ; }
}

interface IB<out T>
{
    T Get();
}

class B<T> : IB<T>
{
    private T _t;
    public void Set(T t) { _t = t; }
    public T Get() { return _t; }
}

■ C# 5.0 での新機能

・非同期処理 (async/await)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/async/
 同期メソッドの作成とほぼ同様の容易さで、非同期メソッドを作成できる。
 Mac の場合は、プロジェクトテンプレートで「.NET Core > App > Console Application」ではなく、「その他 > .NET > コンソールプロジェクト」で作成し、NuGetMicrosoft HTTP Client Libraries を追加してください。
 ※環境によって下記サンプルがエラーとなります。その場合、別の非同期メソッドで確認してください。

static void Main(string[] args)
{
    Method();               // 同期メソッドでの取得
    MethodAsync().Wait();   // 非同期メソッドでの取得

    Console.ReadKey();
}

/// <summary>
/// 非同期メソッドでの取得
/// </summary>
/// <returns></returns>
static async Task<string> MethodAsync()
{
    Console.WriteLine("Start MethodAsync");

    var client = new System.Net.Http.HttpClient();
    var html = await client.GetStringAsync("http://rksoftware.hatenablog.com/");

    Console.WriteLine(html.Take(100).ToArray());

    Console.WriteLine("End MethodAsync");

    return html;
}

/// <summary>
/// 同期メソッドでの取得
/// </summary>
static void Method()
{
    Console.WriteLine("Start Method");

    var client = new System.Net.WebClient();
    client.DownloadStringCompleted +=
        (object sender, System.Net.DownloadStringCompletedEventArgs e) =>
        {
            Console.WriteLine("Start Client_DownloadStringCompleted");

            Console.WriteLine(new string(e.Result.Take(100).ToArray()));

            Console.WriteLine("End Client_DownloadStringCompleted");
        };
    client.DownloadStringAsync(new Uri("http://rksoftware.hatenablog.com/"));

    Console.WriteLine("End Method");
}

・呼び出し元情報の属性 (CallerMemberName など)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/caller-information
 メソッドの呼び出し元の情報を省略可能な引数として取得できる。

static void Main(string[] args)
{
    MyMethod();         // message: message
                        // member name: MyMethod
                        // source file path: XXXXX\XXXXX\Program.cs
                        // source line number: 21
    MyProperty = 10;    // MyProperty changed: 0-> 10

    Console.ReadKey();
}

static void MyMethod()
{
    WriteTrace("message");
}

private static int _myProperty;
static int MyProperty {
    get { return _myProperty; }
    set { WritePropertyChanged(_myProperty, value); _myProperty = value; }
}

static void WriteTrace(string message,
        [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
        [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
        [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
    Console.WriteLine(string.Format("message: {0}", message));
    Console.WriteLine(string.Format("member name: {0}", memberName));
    Console.WriteLine(string.Format("source file path: {0}", sourceFilePath));
    Console.WriteLine(string.Format("source line number: {0}", sourceLineNumber));
}

static void WritePropertyChanged(object oldValue,
    object newValue,
    [System.Runtime.CompilerServices.CallerMemberName]string name = "")
{
    Console.WriteLine(string.Format("{0} changed: {1} -> {2}", name, oldValue, newValue));
}

■ C# 6.0 での新機能

・挿入文字列
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/interpolated-strings
 含まれる挿入式をその文字列表現に置き換えた文字列を返す。

string firstName = "埼玉";
string lastName = "太郎";
Console.WriteLine($"{firstName} {lastName}");   // 埼玉 太郎

Console.ReadKey();

・自動実装プロパティ get アクセサーのみの宣言
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties
  get アクセサーのみを宣言し、変更できないプロパティの作成できる。

class Program
{
    static void Main(string[] args)
    {
        MyClass c = new MyClass();
        int i = c.MyProperty;   // <- これはできる
        // c.MyProperty = 20; <- これはできない

        Console.ReadKey();
    }
}

class MyClass
{
    internal int MyProperty { get; }
    public MyClass()
    {
        MyProperty = 10;    // コンストラクター内では初期化できる
    }
}

・自動実装プロパティの初期化
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties
 フィールドと同様に自動実装プロパティを初期化することができる。

static void Main(string[] args)
{
    int i = MyProperty;
    Console.WriteLine(i);   // 10

    Console.ReadKey();
}

static int MyProperty { get; set; } = 10;

・式形式のメンバー
 https://docs.microsoft.com/ja-jp/dotnet/csharp/methods#expr
 メソッドの本文が 1 つのステートメントの場合、=> を使用した構文ショートカットが使用できる。

static void Main(string[] args)
{
    MyClass c = new MyClass();
    c.FirstName = "埼玉";
    c.LastName = "太郎";
    Console.WriteLine(c.ToString());   // 埼玉 太郎

    Console.ReadKey();
}

class MyClass
{
    internal string FirstName { get; set; }
    internal string LastName { get; set; }
    public override string ToString() => $"{FirstName} {LastName}";
}

・get アクセサーのみの自動実装プロパティ を式本体の定義
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/properties
 読み取り専用プロパティの場合に get アクセサーを式形式のメンバーとして実装できる。

static void Main(string[] args)
{
    FirstName = "埼玉";
    LastName = "太郎";
    Console.WriteLine(FullName);   // 埼玉 太郎

    Console.ReadKey();
}

static string FirstName { get; set; }
static string LastName { get; set; }
static string FullName => $"{FirstName} {LastName}";

・using static ディレクティブ
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/using-static
 static メソッドなどへアクセスする際、名前空間と同様に型名も指定せずに使用できる using の記述ができる。

using System;
using System.Threading.Tasks;
using static System.Math;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Math.PI); // 3.14159265358979
            Console.WriteLine(PI);      // 3.14159265358979
            Console.ReadKey();
        }
    }
}

・Null 条件演算子
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/null-conditional-operators
 メンバー アクセスおよびインデックス操作のチェーンの 1 つの演算が null を返す場合、チェーンの実行の残りの部分を停止し null を返すショートサーキット。

string s = "text";
Console.WriteLine(s.Length.ToString()); // 4
Console.WriteLine(s.ToCharArray()[0]); // t

s = null;
try
{
    Console.WriteLine(s.Length.ToString()); // 例外発生
    Console.WriteLine(s.ToCharArray()[0]);
}
catch (NullReferenceException ex)
{
    Console.WriteLine(ex.Message);
}

Console.WriteLine(s?.Length.ToString() == null); // True
Console.WriteLine(s?.ToCharArray()[0] == null);  // True

Console.ReadKey();

・nameof
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/nameof
 変数、型、またはメンバーの名前を文字列として取得できる。

class Program
{
    static void Main(string[] args)
    {
        int variable;

        Console.WriteLine(nameof(Program));     // Program
        Console.WriteLine(nameof(variable));    // variable
        Console.WriteLine(nameof(MyMethod));    // MyMethod
        Console.WriteLine(nameof(MyProperty));  // MyProperty

        Console.ReadKey();
    }

    static void MyMethod() { ; }
    static int MyProperty { get; set; }
}

・例外をフィルター処理する述語式 (when)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/try-catch
 処理対象の例外をフィルターする際に、例外オブジェクトの型の評価に続いて、述語式を使用して例外をフィルターできる。

static void Main(string[] args)
{
    MethodA(null, "text");  // text1:Null または Empty です
                            // パラメーター名: text1
    MethodA("text", null);  // text2:Null または Empty です
                            // パラメーター名: text2
    Console.ReadKey();
}
static void MethodA(string text1, string text2)
{
    try
    {
        MethodB(text1, text2);
    }
    catch (ArgumentException ex) when (ex.ParamName == "text1")
    {
        Console.WriteLine($"{nameof(text1)}:{ex.Message}");
    }
    catch (ArgumentException ex) when (ex.ParamName == "text2")
    {
        Console.WriteLine($"{nameof(text2)}:{ex.Message}");
    }
}

static void MethodB(string text1, string text2)
{
    if (string.IsNullOrEmpty(text1)) throw new ArgumentException("Null または Empty です", nameof(text1));
    if (string.IsNullOrEmpty(text2)) throw new ArgumentException("Null または Empty です", nameof(text2));
}

・catch/finally ブロック内での await
 https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-6#await-in-catch-and-finally-blocks(英語)
 catch および finally ブロックで await による非同期処理ができる。
 Mac の場合は、プロジェクトテンプレートで「.NET Core > App > Console Application」ではなく、「その他 > .NET > コンソールプロジェクト」で作成し、NuGetMicrosoft HTTP Client Libraries を追加してください。
 ※環境によって下記サンプルがエラーとなります。その場合、別の非同期メソッドで確認してください。

static void Main(string[] args)
{
    MethodAsync().Wait();   // 非同期メソッドでの取得

    Console.ReadKey();
}

static async Task<string> MethodAsync()
{
    var client = new System.Net.Http.HttpClient();
    var html = await client.GetStringAsync("http://rksoftware.hatenablog.com/about");
    try
    {
        html = html.Substring(0, int.MaxValue);
    }
    catch(ArgumentOutOfRangeException ex)
    {
        Console.WriteLine(ex.Message);
        html = await client.GetStringAsync("http://rksoftware.hatenablog.com/about");
        html = new string(html.Take(100).ToArray());
    }

    Console.WriteLine(html);

    return html;
}

・インデックス初期化 (Dictionary 初期化)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers
 単純なコレクションだけでなく、作成をサポートするコレクション(Dictionary)も初期化子を指定できる。

List<string> list = new List<string>
{
    "saitama",
    "gunma",
    "ibaraki"
};

Dictionary<int, string> dictionary = new Dictionary<int, string>
{
    [101] = "saitama",
    [201] = "gunma",
    [301] = "ibaraki"
};

Console.WriteLine(list[0]);         // saitama
Console.WriteLine(dictionary[201]); // gunma

Console.ReadKey();

・拡張メソッドでコレクション初期化子
 https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-6#extension-add-methods-in-collection-initializers(英語)
 Add メソッドが拡張メソッドで定義されている型も、初期化子を指定できる。

class Program
{
    static void Main(string[] args)
    {
        MyChass c = new MyChass() { "saitama", "gunma", "ibaraki" };
        Console.WriteLine(c);   // saitama, gunma, ibaraki

        Console.ReadKey();
    }
}

class MyChass: IEnumerable<string>
{
    private List<string> _texts = new List<string>();

    internal void Push(string text)
    {
        _texts.Add(text);
    }

    public override string ToString()
    {
        return string.Join(", ", _texts);
    }

    public IEnumerator<string> GetEnumerator()
    {
        return _texts.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _texts.GetEnumerator();
    }
}

static class MyClassExtension
{
    internal static void Add(this MyChass c, string text)
    {
        c.Push(text);
    }
}

・オーバーロードの解決の改善
 https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-6#improved-overload-resolution(英語)
 オーバーロードで引数に Action を受け取るメソッドと Func を受け取るメソッドがある場合のコンパイラによる選択が改善した。

static void Main(string[] args)
{
    Task.Run(() => DoMethod()); // 以前はこう書く必要があった
    Task.Run(DoMethod);         // 今はこう書ける

    Console.ReadKey();
}

static Task DoMethod()
{
    Console.WriteLine("DoMethod");
    return Task.FromResult(0);
}

■ C# 7.0 での新機能

・out 変数をメソッド呼び出しの引数リスト内で宣言 (Out variables)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/out-parameter-modifier
 out 引数を含むメソッドの呼び出しの際に、out 変数を、メソッド呼び出しの引数リスト内で宣言できる。

string text = "10";
if (!int.TryParse(text, out int i))
{
    Console.WriteLine($"{text} を int にパースできませんでした");

    Console.ReadKey();
    return;
}
Console.WriteLine($"{text} を int: {i} にパースできました");  // 10 を int: 10 にパースできました

Console.ReadKey();

・タプル (Tuples)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/tuples
 複数の値を 1 つのオブジェクトに簡単にパッケージできる。 [注意] この機能を使用するには NuGetSystem.ValueTuple を追加する必要がある。

static void Main(string[] args)
{
    string text;
    int length;
    (text, length) = MethodA();

    var tupleA = MethodA();
    var tupleB = MethodB();

    Console.WriteLine($"{text}, {length}");                 // textA, 5
    Console.WriteLine($"{tupleA.Item1}, {tupleA.Item2}");   // textA, 5
    Console.WriteLine($"{tupleB.text}, {tupleB.length}");   // textB, 5

    Console.ReadKey();
}

static (string, int) MethodA()
{
    string text = "textA";
    return (text, text.Length);
}

static (string text, int length) MethodB()
{
    string text = "textB";
    return (text:text, length: text.Length);
}

・分解 (Deconstruction)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/tuples
 タプルを"分解"し項目を展開した状態で、変数を作成できる。

static void Main(string[] args)
{
    // 分解していない書き方
    {
        var tuple = Method();
        Console.WriteLine($"{tuple.text}, {tuple.length}");     // text, 4
    }
    // 以降、分解している書き方
    {
        var (text, length) = Method();
        Console.WriteLine($"{text}, {length}");                 // text, 4
    }
    {
        (var text, var length) = Method();
        Console.WriteLine($"{text}, {length}");                 // text, 4
    }
    {
        (string text, int length) = Method();
        Console.WriteLine($"{text}, {length}");                 // text, 4
    }

    Console.ReadKey();
}

static (string text, int length) Method()
{
    string text = "text";
    return (text: text, length: text.Length);
}

・パターンマッチ (Pattern matching)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/pattern-matching
 is および switch で型を判断しての処理が簡潔に書ける。

static void Main(string[] args)
{
    string textIs = IsPattern(10);
    Console.WriteLine(textIs);              // 引数は int の 10 です

    string textSwitch = SwitchPattern("10");
    Console.WriteLine(textSwitch);          // 引数は string の 10 です

    Console.ReadKey();
}

static string IsPattern(object arg)
{
    if (arg is int i) { return $"引数は int の {i} です"; }
    if (arg is double d) { return $"引数は double の {d} です"; }
    if (arg is string s) { return $"引数は string の {s} です"; }
    return "unknown";
}

static string SwitchPattern(object arg)
{
    switch (arg)
    {
        case int i: return $"引数は int の {i} です";
        case double d: return $"引数は double の {d} です";
        case string s: return $"引数は string の {s} です";
        default: return "unknown";
    }
}

・参照返り値と参照ローカル変数 (Ref returns and locals)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-7#ref-locals-and-returns(英語)
 値型の参照を返り値として返すことができる。

static void Main(string[] args)
{
    // 単純な値
    int value = 10;
    ref int vr = ref Method(ref value);

    Console.WriteLine($"{value}, {vr}"); // 11, 11
    value = 20;
    Console.WriteLine($"{value}, {vr}"); // 20, 20
    vr = 30;
    Console.WriteLine($"{value}, {vr}"); // 30, 30

    // 配列の要素
    int[] values = { 10, 20 };
    ref int sr = ref Method(values);

    Console.WriteLine($"{values[0]}, {sr}"); // 10, 10
    values[0] = 20;
    Console.WriteLine($"{values[0]}, {sr}"); // 20, 20
    sr = 30;
    Console.WriteLine($"{values[0]}, {sr}"); // 30, 30

    Console.ReadKey();
}

static ref int Method(ref int arg)
{
    arg++;
    return ref arg;
}

static ref int Method(int[] arg)
{
    return ref arg[0];
}

・ローカル関数 (Local functions)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-7#local-functions(英語)
 メソッド内でメソッドを定義できる。

static void Main(string[] args)
{
    LocalFunctionSample();  // 1
                            // 2
                            // 3
    OldStyleSample();       // 1
                            // 2
                            // 3

    Console.ReadKey();
}

static void LocalFunctionSample()
{
    func(0);
    return;

    // 新しいスタイル
    void func(int v) { Console.WriteLine(++v); if (v < 3) { func(v); } }
}

static void OldStyleSample()
{
    // 以前のスタイル
    Action<int> func = null;
    func = v => { Console.WriteLine(++v); if (v < 3) { func(v); } };

    func(0);
    return;
}

・式形式のメンバーの追加 (More expression bodied members)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members(英語)
 コンストラクター、ファイナライザー、プロパティの settergetter、メソッドを式で定義できる。

class Program
{
    static void Main(string[] args)
    {
        MyClass a = new MyClass("saitama");
        MyClass b = new MyClass("gunma");
        Console.WriteLine(object.Equals(a, b));    // False

        Console.ReadKey();
    }
}

class MyClass
{
    internal MyClass(string value) => _value = value;
    ~MyClass() => Value = null;

    private string _value;
    internal string Value
    {
        get => _value;
        set => _value = value;
    }

    public override bool Equals(object obj) => obj is MyClass c && this.Value == c.Value;
    public override int GetHashCode() => base.GetHashCode();
}

・throw 式 (Throw expressions)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/throw
 throw を、式およびステートメントとして使用できる。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            MyClass c = new MyClass(null);
        }
        catch(ArgumentException ex)
        {
            Console.WriteLine(ex.Message);  // 値が有効な範囲にありません。
        }

        Console.ReadKey();
    }
}

class MyClass
{
    internal MyClass(string value) => _value = value ?? throw new ArgumentException();

    private string _value;
    internal string Value
    {
        get => _value;
        set => _value = value ?? throw new ArgumentException();
    }

    internal string GetValue() => throw new ArgumentException();
    internal void SetValue(string value) => _value = value ?? throw new ArgumentException();
}

・一般的な型での非同期メソッドの戻り値 (ValueTask)(Generalized async return types)
 https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#generalized-async-return-types(英語)
 async 非同期メソッド内で非同期処理が必要である場所を通らなかった場合、Task オブジェクトを作らない事ができる。 [注意] この機能を使用するには NuGetSystem.Threading.Tasks.Extensions を追加する必要がある。

static void Main(string[] args)
{
    int a = Func(true).Result;
    int b = Func(false).Result;
    Console.WriteLine(a);
    Console.WriteLine(b);

    Console.ReadKey();
}

static async ValueTask<int> Func(bool b)
{
    if (b) return 0;
    await Task.Delay(1).ConfigureAwait(false);
    return 1;
}

・数値リテラルの表記の改善 (Literal improvements)
 https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-7#numeric-literal-syntax-improvements(英語)
 2進数表記でのリテラルを記述できる。数値リテラルの桁数などをわかりやすく記述できる。

int nenshu= 50_000_000;
Console.WriteLine($"年収 {nenshu} 億円");

int shoyo = 0b0100_0110_0101;
Console.WriteLine($"賞与 {shoyo} 万円");

Console.ReadKey();

■ 出典/参考文献

 上記まとめの作成に、次のサイトを大いに参考にさせていただきました。
 文言やサンプルコードは C# のガイド のコードを利用させていただきました。

C# のガイド | Microsoft Docs

C# によるプログラミング入門 | ++C++; // 未確認飛行 C