【C#】メソッド呼び出し側が、処理自体を渡す方法(メソッドが引数、無名メソッド)

ファイル名: -

 

Lispや JavaScriptで簡単につかえる引数部分に関数を渡すやり方です。
 For loopの一部の処理だけ変更したい時、2つの大きなメソッドを作りたくもありません。
そういうときに難しいですが少し使えます。手間もLispに比べて下準備が必要なのがネックです。

使い分け

使い分けパターン 使用クラス ポイント

メソッドを引数に渡す方法

 

Func

<高階関数>

呼出先で、処理の切替を意識したくない時(呼び出し元で処理しておきたい時)に使える。
内部簡易メソッド

delegate+Funcなど

<ラムダ>

インナーメソッドなので変数を同じスコープで引き継ぎつつ内部の処理のようにかけるが、正式なメソッドのように引数をくどくきっちり渡さずに済む。
 Java版はこちら

メソッドを引数として渡す方法

シーケンシャルに順番に処理されるのであれば、今までのやり方で不満はないが、その呼出先の真ん中で、ちょっと処理を変えたい時けど、呼出先では意識したくない時などに重宝する。

(A→X→B ) の処理と  (A→Y→B)みたいな処理。 業務タスクを処理する塊があったとして、 夜の場合の処理の仕方のメソッドを渡すか、日中帯の処理のメソッドを渡すかして、処理を少し呼び出し先の処理中に切り替えたい時に使う。
これでX,Yの部分のコーディングがすっきりする。関数を引数としても渡せることがLispの生産性の強み でもあったし、人工知能で世代間を書こうとすると遺伝アルゴリズムをそのまま渡すこの方式が一番書きやすい。

3ステップ

1.呼び出される(引数として渡される)メソッドはいつもの様に普通に宣言する(X or Y)。

X or Y :

 private int add(int v1,int v2){ ... } のような関数が定義されている想定。

2.メソッドを引数に取る時の引数の定義を、メソッド型で宣言するだけ(A → B呼び出し時用)

B上の定義

private static void totalDeal(Func<int, int, int> calc){ ... }

↑これは足し算/引き算のように、引数2つ取り、戻り値が int(3番目)のメソッドを calcという名前で受け付ける場合。

3.呼び出す(A → B時)

A上の定義

totalDeal(add);  

Funcで事足りると思うけれども、例えば戻り値をVoidにしたい場合、Actionを使う。

4.普通にもらった引数名をメソッドのように使う

B上
private static void totalDeal(Func<int, int, int> calc){

    calc(x1,x2);

}

JavaScriptなどのスクリプト言語で型が自由だと、よくありますよね。

さらにに省エネ

1ステップ目と3ステップ目をあわせる、無名メソッド(ラムダ式)の活用技がある。
3の時に、例のあの時の動作はこれね♪って、その場で作ったメソッドを渡しちゃうパターン。

totalDeal( (int v1 , int v2) => { return v1 + v2} ;);

 

内部簡易メソッド:無名メソッド

メソッドを呼ぶよりも、変化部分だけ引数で変えて渡すことができる引数の引き渡しを最小限にしてメソッドの恩恵を得られ、インナーメソッドのあるメソッドの中で整理しつつ名前をなしにできるところが楽できるポイント。インナーメソッドのようにメソッドの内部で微妙に繰り返したい時、メソッドを正式に作るより楽にできる。

使用の原則

3ステップ

1.delegateでClassを作る。メソッドのように型(返り値と引数)を宣言する。
delegate void MessageOut(string message);

※ delgate の前に public/private 等記載可能

2.インスタンス作成のように、メソッドの内部を定義する。
action インスタンスをMessageOut型で作り、 具体的な動作内容は  括弧の中:Console ~~部分。

※ このたびに作らなくても、すでに定義された 同じ型のメソッド(OutVoidStr とする)があれば、  MessageOut action = this.OutVoidStr; のようにも指定可能。

JavaScriptなどのスクリプト言語で型が自由だと、よくありますよね。

 

3.先ほど実体化インスタンス経由で動作させる。

action("Hello! World!");

つまり、同じ型void/Stringであれば、このような感じで
何種類も必要に応じてその場で作ることが可能になります。
実際 delegateの宣言時は中身は指定していないですよね。

Action デリゲート (System) 

他の定義例(メソッドっぽいdelegate先の型)

int 戻り値に、 int,intが引数の場合

delegate int sumTwo(int x, int y);

引数を増やしたり、片方だけ string や bool等にすることも可能です。

int(3つ目)が戻り値で、  (int, int)が引数の場合のFuncの定義

Func<int,int,int> func;

もう少し省エネ

クラスを宣言するのであれば、無名の意味がほぼ無いです。
C#では、System.Action<>の定義があり、さらに最初のdelegateを省略できます。

今回の 場合 1ステップ目を省略し、3ステップ目は変更ありません。
void , stringのケース

Action<string> action = delegate(string msg){   ...  }


その他の一時メソッド作成例パターン:

目的の切り分け (例えば的な意味で)
事前定義されている型
戻り値あり、引数も自由自在:一番抽象的なメソッド  Func
戻り値 Void実行するだけのメソッド Action
戻り値bool、引数1個:属性を断定する。
いわゆる isVoid()  , isValid() , isNumeric()的な使い方
Predicate
戻り値int 引数2つ:ソートなどで使う比較 Comparison
戻り値任意、引数1つ:変換処理 Converter

ラムダ式

ラムダ式とは一般にメソッドの型を以下のように表現したもの。メソッドの中にメソッドがかける。しかもスコープは元と連携できて変数を外のものをそのまま中で利用できる。引数で渡す必要もない。
(引数) => { 式の表現;  式の表現; ... }

例:
(int x , int y) => {  return x + y;};

これが、更に省略されて
(x,y)=> x+y
にもなる。

引数がない場合 最初のカッコも () => x+y; の形になる。

曖昧な表現になりそうだったらカッコ等で囲みましょう。

ラムダ式 (C# プログラミング ガイド)

ここまで来たら以下のプログラムが読めるようになると思います。

引数をどう返すか部分はラムダ式の中にあり、Countは配列の要素それぞれ 処理し
この場で定義した nに渡して boolean値を返します。
Countはそれを受け処理します。 例えばtrueの場合 +1します。結果奇数の数が求まります。

つまり、条件をOK/NGと判断する実際のロジックをその場で作って、引数として渡しています。

引数を受けて一つ一つに分解しているのは Countですが、
カウントアップする条件は引数でもらうメソッドに任せてます。これは一種のポリモーフィズムです。
sort関数/event listener関数などでよく見る形だと思います。

おまけ

ちなみに内部メソッド(ラムダ)のスコープはその上のメソッドのスコープも対象範囲になります(ラムダ式を読んでいるメソッド内のスコープも対象になる)。

関連ページリンク

アプリケーションの設定を保存しておく方法【C#】

デバッグや通知に便利なメッセージ通知方法の使い分け【C#】

座標操作のメソッドの使い分け【C#】

クリップボード操作方法の原則【C#】

アイコンイメージ3つの取得方法サンプルプログラム【C#】

キーエミュレート送信のまとめ【C#】【覚書メモ】

Delicious にシェア
Digg にシェア
reddit にシェア
LinkedIn にシェア
LINEで送る
email this
Pocket




コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です