Mono 5.0 と C# 7 の Local Functions, Tuples で遊ぶ

Mono 5.0 と C# 7 で遊びます。

まずは、mono のアップグレード。

$ brew upgrade mono

mono のバージョンの確認。

$ mono --version
Mono JIT compiler version 5.0.1.1 (2017-02/5077205 Wed May 31 14:47:54 BST 2017)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com

csc コマンドでコンパイルして実行します。

$ cat a.cs
using System;

class Program {
    static void Main() {
        Console.WriteLine("Hello C# 7");
    }
}

$ csc a.cs
Microsoft (R) Visual C# Compiler version 2.0.0.61404
Copyright (C) Microsoft Corporation. All rights reserved.

$ mono a.exe
Hello C# 7

C# 7 で遊ぶ準備が整いました。

Local Functions

メソッド名と同じ名前のローカル関数を定義できるようです。 以下では、メソッド名と同じ Add, Sub という名前のローカル関数を定義しています。

using System;

class Program {
    static int Add(int a, int b) {
        int Add(int x, int y) {
            return x + y;
        }
        return Add(a, b);
    }

    static int Sub(int a, int b) {
        int Sub(int x, int y) => x - y;
        return Sub(a, b);
    }

    static void Main() {
        Console.WriteLine(Add(10, 23));  // 33
        Console.WriteLine(Sub(100, 88)); // 12
    }
}

ローカル関数の引数には、外側のメソッドと同じ引き数名は付けられないようです。

using System;

class Program {
    static int Add1(int n) {
        // コンパイルエラー: 引数名 n は外側のメソッドで使われている
        int Add1(int n) => n + 1;
        return Add1(n);
    }

    static void Main() {
    }
}

コンパイルすると、以下のコンパイルエラーが表示されます。

$ csc a.cs
a.cs(6,22): error CS0136: A local or parameter named 'n' cannot be declared in this scope 
because that name is used in an enclosing local scope to define a local or parameter

Tuples

using System;

class Program {
    static void Main() {
        var t = ("Bob", 10);
        Console.WriteLine($"{t.Item1} {t.Item2}"); // Bob 10

        var t2 = (Name: "Bob", Age: 10);
        Console.WriteLine($"{t2.Name} {t2.Age}"); // Bob 10

        (string name, int age) = ("Bob", 10);
        Console.WriteLine($"{name} {age}"); // Bob 10

        (string Name, int Age) bob = ("Bob", 10);
        Console.WriteLine($"{bob.Name} {bob.Age}"); // Bob 10

        var (name2, age2) = ("Bob", 10);
        Console.WriteLine($"{name2} {age2}"); // Bob 10
    }
}

メソッドの戻り値にも使えますので、複数の値を返すときにわざわざ新しい型を作る必要がなくなります。

using System;

class Program {
    // a を b で割ったときの商と余りを返す
    static (int Quotient, int Remainder) DivMod(int a, int b) {
        return (a / b, a % b);
    }

    static void Main() {
        (int q, int r) = DivMod(10, 3);
        Console.WriteLine($"{q} {r}"); // 3 1

        var x = DivMod(11, 3);
        Console.WriteLine($"{x.Quotient} {x.Remainder}"); // 3 2

        var (q2, r2) = DivMod(12, 3);
        Console.WriteLine($"{q2} {r2}"); // 4 0
    }
}

参考