やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

C#の概念 メソッド

 細かい便利機能が多い。

成果物

情報源

コード

シグネチャ

  • public/private/protectedなど
  • abstract/sealedなど
  • メソッド名
  • 戻り値
  • 引数

 デリゲートは「戻り値」もシグネチャに含む。ただしオーバーロードするとき「戻り値」はシグネチャに含まない。

オーバーロード

class C {
    public void M() {}
    public void M(int a) {}
}
class C {
    public void M() {}
    public int M() {} // error CS0111
}

 戻り値だけが違うとオーバーロードできない。同じメソッドと判断されてエラー。

デリゲート

  delegate void A();
//delegate int  A(); // 変数名を他と同一にすると error CS0102 になる
  delegate int  B();

呼出

引数と戻り値の有無パターン4つ

 引数なし。戻り値なし。

class Main {
    public void Run() {
        A();
    }
    public void A() => Console.WriteLine("A");
}

 引数なし。戻り値あり。

class Main {
    public void Run() {
        int f = F();
        Console.WriteLine($"{f}");
    }
    public void F() => 100;
}

 引数あり。戻り値なし。

class Main {
    public void Run() {
        A(1);
    }
    public void A(int p) => Console.WriteLine($"A: p={p}");
}

 引数あり。戻り値あり。

class Main {
    public void Run() {
        Console.WriteLine($"{F(2)}");
    }
    public int F(int p) { Console.WriteLine($"F: p={p}"); return 101; }
}

 まとめると以下。

class Main {
    public void Run() {
        A();
        int f = F();
        Console.WriteLine($"{f}");
        A(1);
        Console.WriteLine($"{F(2)}");
    }
    public void A() => Console.WriteLine("A");
    public int  F() => 100;
    public void A(int p) => Console.WriteLine($"A: p={p}");
    public int  F(int p) { Console.WriteLine($"F: p={p}"); return 101; }
}

引数パターン

  • 位置引数
  • 名前付き引数

位置引数

class Main {
    public void Run() {
        A(1, 2);
    }
    public void A(int p1, int p2) => Console.WriteLine($"A: p1={p1}, p2={p2}");
}

 複数の位置引数があるときは、カンマ区切りで順に指定する。

名前付き引数

 順序不定にできる。

class Main {
    public void Run() {
        P(p2:2, p1:1);
    }
    public void P(int p1, int p2) => Console.WriteLine($"P: p1={p1}, p2={p2}");
}

 ただし名前付き引数は位置引数より後ろに書かねばならない。もし位置が一致しているならOK。

P(p2:2, 3);    // error CS8323
P(p1:1, 2);    // P: p1=1, p2=2
class Main {
    public void Run() {
        P(1, 2);       // P: p1=1, p2=2
        P(p2:2, p1:1); // P: p1=1, p2=2
        P(1, p2:2);    // P: p1=1, p2=2
        P(p1:1, 2);    // P: p1=1, p2=2
        P(p2:2, 3);    // error CS8323

        N(1, 2);       // N: p1=1, p2=2
        N(p2:2, p1:1); // N: p1=1, p2=2
        N(1, p2:2);    // N: p1=1, p2=2
        N(p1:1, 2);    // N: p1=1, p2=2
        N(p2:2, 3);    // error CS8323
    }
    public void P(int p1, int p2) => Console.WriteLine($"P: p1={p1}, p2={p2}");
    public void N(int p1=0, int p2=0) => Console.WriteLine($"N: p1={p1}, p2={p2}");
}
オプション引数
class Main {
    public void Run() {
//        O();           // error CS7036
        O(1);          // O: p1=1, o1=0
//        O(o1:2);       // error CS7036
        O(1, o1:2);    // O: p1=1, o1=2
        O(p1:1, o1:2); // O: p1=1, o1=2
        O(o1:2, p1:1); // O: p1=1, o1=2
    }
    public void O(int p1, int o1=0) => Console.WriteLine($"O: p1={p1}, o1={o1}");
}

 オプション引数は省略できる。省略すると定義時の値となる。

 だが位置引数は省略できない。

 位置引数、オプション引数の順で定義する必要がある。

public void O2(int o=0, int p) => Console.WriteLine($"O: p={p}, o={o}"); // error CS1737

継承メソッドとoverrideメソッド

 すべてのクラスはObjectクラスを継承する。Objectクラスに定義されてあるEqualsメソッドなどが使える。

class C {}
var c = new C();
c.Equals(c);

 overrideすると以下。

class Main {
    class C {
        public override bool Equals(object obj) {
            Console.WriteLine("比較した");
            return base.Equals(obj);
        }
    }
    public void Run() {
        var c = new C();
        c.Equals(c);
    }
}

値渡しと参照渡し

class Main {
    public void Run() {
        int i = 1;
        PassValue(i);
        Console.WriteLine($"i={i}");

        int[] a = new int[1] { 1 };
        PassReference(a);
        Console.WriteLine($"a[0]={a[0]}");
    }
    private void PassValue(int v) { v = 2; }
    private void PassReference(int[] r) { r[0] = 2; }
}
i=1
a[0]=2

 関数内で引数に値をセットする。その後、呼出元で引数の値をみてみる。

 値型は変化せず。参照型は変化した。

 値型も参照として渡すことができる。

class Main {
    public void Run() {
        int i = 1;
        PassValue(ref i);
        Console.WriteLine($"i={i}");
    }
    private void PassValue(ref int v) { v = 2; }
}
i=2

 refの他にもout,inがある。

class Main {
    public void Run() {
        int i = 1;
        PassValueR(ref i);
        Console.WriteLine($"i={i}");

        int k;
//        PassValueR(out k); // error CS1620
        PassValueO(out k);
        Console.WriteLine($"k={k}");

        PassValueI(in i);
    }
    private void PassValueR(ref int v) { v = 2; }
    private void PassValueO(out int v) { v = 2; }
//    private void PassValueO(out int v) { } // error CS0177
    private void PassValueI(in  int v) { Console.WriteLine($"in v={v}"); }
//    private void PassValueI(in  int v) { v = 2; } // error CS8331
}

 outは引数が事前に初期化されていなくてもOK。refは初期化必須。

 inは代入不可。refは代入可。outは代入必須。

 参照型にも使える。

可変長引数(params

class Main {
    public void Run() {
        Params();
        Params(1);
        Params(1,3,5);
    }
    private void Params(params int[] args) {
        Console.WriteLine(String.Join(",", args));
    }
}

戻り値

class Main {
    public void Run() {
        Console.WriteLine($"{A()}");
        Console.WriteLine($"{B()}");
        Console.WriteLine($"{C()}");
    }
    private int A() { return 1; }
    private int B() => 2;
    private (int,int) C() => (1,2);
}

拡張メソッド

class Main {
    public void Run() {
        string s = "Hello Extension Methods";  
        Console.WriteLine($"{s.WordCount()}");
    }
}
public static class MyExtensions
{
    public static int WordCount(this String str)
    {
        return str.Split(new char[] { ' ', '.', '?' }, 
                         StringSplitOptions.RemoveEmptyEntries).Length;
    }
}   

 string型にWordCountメソッドを追加した。

非同期メソッド

class Main {
    public void Run() {
        M().Wait();
    }
    private async Task M() {
        await Task.Delay(2000);
        Console.WriteLine($"非同期メソッド!");
    }
}

class Main {
    public void Run() {
        A();
        C();
    }
    private void A() => Console.WriteLine("A");
//    private void B() => { } // error CS1525,CS1002,CS1519
    private void C() { Console.WriteLine("C"); }
}

 1行だけならラムダ式でも書ける。

イテレータ

class Main {
    public void Run() {
        foreach (int i in A()) {
            Console.WriteLine($"i={i}");
        }
    }
    private IEnumerable<int> A() {
        yield return 1;
        yield return 3;
        yield return 5;
    }
}

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux