ラムダ式の初歩を踏み外して進んできてしまった事に割と最近気づいた、と言う恥ずかしい話です。
今時点のまとめを。
もし同じような悩みにぶつかった人の助けになることを願って。
ラムダ式をAction/Funcの指定をしないで書くときの引数や返り値の理解が曖昧でした。
- Actionは返り値が
無い無くてよい(有っても無視) ≒ void メソッド - Funcは返り値がある
Action
引数なし
//コンソールに文字を出力
() =>
{
Console.WriteLine("Action");
};
//これも同じ
() => Console.WriteLine("Action");
//↑はこんな風に割り当てられてるfを匿名で使うんです
Action f = () => Console.WriteLine("Action");
//メソッドで記述するとこれと同等
void 匿名()
{
Console.WriteLine("Action");
}
引数が一個
2019.02.02 @sdkei 様 のご指摘から匿名型の記述を変更
//引数1個をaと言う名前で渡すAction
a =>
{
Console.WriteLine("a is " + a.ToString());
};
//これも同じ
(a) =>
{
Console.WriteLine("a is " + a.ToString());
};
//↑はこんな風に割り当てられてるfを匿名で使うんです
Action f = (a) => Console.WriteLine("a is " + a.ToString());
////メソッドで記述するとこれと同等
//void 匿名(dynamic a)//dynamicよりもvarが適当??でも引数型に指定できないのでdynamicで
//{
// Console.WriteLine("a is " + a.ToString());
//}
//メソッドで記述するとこれと同等
void 匿名<T>(T a)
{
Console.WriteLine("a is " + a.ToString());
}
私は「_」が特別なものと思い込んでおりちょっと混乱しました。
@albireo 様からの指摘がありました。
C#7.0以降では明確に制限を付けることが出来ます。
ラムダ式の引数の「_」はコンパイラに「これは使うつもりのない引数です」と教えることでエラーチェックするためのもので、もし「_を右辺で参照していてもエラー扱いしない」というコンパイラオプションがあれば実行時には普通のパラメータ変数とまったく同じ扱いになる。
と言う事でコンパイラオプションで「_ 」を使うとエラーにすることも出来るようです。
よって明確に「使わない」と言う意思を持って「_」にしましょう。と言うことですか。
//引数1個だけど使わないAction
//「_」で表現される引数は使わない、と言う意思表示(コンパイラオプションでエラーに出来る)
_ =>
{
Console.WriteLine("Action");
//コンパイルオプションで「エラーにする」設定が無ければ
//「_」で変数名は割り当てられているので使える
//Console.WriteLine(_);
//使わない変数が複数個あると全て「_」では宣言できない
//ので、複数個「_」をつなげて表現する。
//hoge.Click += (_,__) =>{};
};
引数2個以上
引数を「,」で区切ればOKです。
(a,b) =>
{
Console.WriteLine("a is " + a.ToString();
Console.WriteLine("b is " + b.ToString();
};
Func
必ず返り値が必要。
式形式の場合はreturn不要
ステートメント形式の場合はreturn句が必要です。
//式形式
() => a == b;
() => a.ToString();
//ステートメント形式
() =>
{
return a == b;
};
() =>
{
return a.ToString();
};
引数なし
Func<TResult>
型にしなければなりません。
//Func<bool>型の必ずtrueを返すやつ
() => {return true;};
//これも同じ
() => true;
//↑はこんな風に割り当てられてるfを匿名で使うんです
Func<bool>f = () => true;
引数1個以上
Func<T1,T2,・・・,Tresult>
ですね。
//aがhogeクラスか調べる
(a) => {return a is hoge;}
//これも同じ
(a) => a is hoge;
//↑はこんな風に割り当てられてるfを匿名で使うんです
Func<object, bool> f = (a) => a is hoge;
メソッドで使う場合は型が指定されます
例えば IObserveble<TResult>.Subscribe()
で使う場合は、
引数にAction<TResult>
が指定されるので引数が必要です。
私が躓いたのはReactiveCommand.Subscribe()
でした。
一見引数なしでいけそうですが、ReactiveCommand
はIObservable<object>
なので引数が必要なのです。
class hoge
{
public ReactiveCommand Command ;
public hoge()
{
Command = new ReactiveCommand();
//Command.Subscribe(() => Console.WriteLine("Command 実行!"));//こうではなくて
Command.Subscribe(_ => Console.WriteLine("Command 実行!"));//こうするか
Command.Subscribe(x => Console.WriteLine(x));//こうしてみたり
}
}
因みにこのobjectに何が入ってくるのか・・・?と調べてみたらExecuteした時の引数のようです。
var hogeInstance = new hoge();
hogeInstance.Command.Execute();//これだと引数はnull
hogeInstance.Command.Execute(1);//これだと引数は1
ReactiveCommand<T>
もあるので混乱してしまいました。
因みにばっちり↓にもはまっていたので余計に混乱してました。
https://blog.kazuakix.jp/entry/2015/06/28/233439