数年後見返す俺のために一応メモしておくと、今週アメリカ大統領選挙があってまさかのトランプ勝利で世の中が大混乱です。
これから時代が変わる、そんな時期にいまから書くお話はすごく今更ながらのやつで、多分エッジにいるエンジニアには10年以上前に通過したような話。どうなってんだ。いや、前回は3000年前くらい昔の話書いたんだしどうでもいいか。いいのか?
まあそれはそれとして。
なんか仕事でC#のプログラムをいじる機会があってですね。
よくあるiniファイル解析みたいなやつをゴリゴリ作ってたんですよ
ID=00123
Name=べろべろたかもろち(※)
Price=198
Begin=2016/10/12
End=2016/12/26
こんなファイルのやつね。この情報のうちID=とName=とPrice=の情報だけが欲しいので
解析部分だけ別関数に切り出して処理してやろうとした。この処理部分について心に引っかかることが。
※ 4歳の息子がドラクエのミミックに名付けた名前。多分べろべろが出てる宝箱のことだと思う…
outのキモさ
複数の戻り値を書かなきゃいけないわけだけど例えばRubyなら普通に戻り値を複数用意すればいいだけだよね
id, name, price = parse_lines(line)
C#にそんな器用なことできないから、どうするかっていうと引数に戻り値を書くわけだ。
この書き方にref修飾子とout修飾子があるということを知った。2016年の終わりに。
ParseLine(ref id, ref name, ref price)
ParseLine(out id, out name, out price)
上のうちどっちか。
どっち使えばいいんだよって当然悩むわけだけど、色々調べた結果refは関数呼び出しの外で変数初期化、outは中で変数初期化するの場合に使うのが正しいらしい。
ふむふむ、いやrefの方はなんとなくわかるよ。
string id = "";
string name = "";
int price = 0;
ParseLine(ref id, ref name, ref price);
// id後始末
// name後始末
// price後始末
初期化は呼び出し元で行う。恐らく後始末も呼び出し元で行う(今回は文字列と数値だけなんで特にいらないけど)
でもout修飾子、お前はなんだ?
string id, name;
int price;
ParseLine(out id, out name, out price);
// id後始末
// name後始末
// price後始末
宣言だけ呼び出し元、初期化は呼び出し先、更に後始末は呼び出し元。なんだこのキモさは!!!
これが文字列数値だけならいいけど、複雑なオブジェクトなら絶対初期化終了処理不良起こすだろ!!!
なんでこんなキモい選択肢が存在しているのか、C#のポリシーがかなりわからなくなってきた。いつ使うのこれ?
20世紀の遺物
そもそもこんな構文C#とVB.NETくらいにしか見当たらなくて、他の言語達はどのように解決していったんだろうと
色んなベストプラクティス探してたんだけど、いたるところで言われているのはそもそもrefもoutも使うべきではないよねという至極全うなお話。
アッハイ
そもそも引数に戻り値の役目をさせるのは所詮高級アセンブラにすぎないCでものを書くときの名残でしかなくて、
しかも終了処理で後始末とかを気にするのはガーベージコレクション機能を持っていない前世紀の言語の名残でしかなくて
トランプも大統領になるような時代に何20世紀の話をしているんだという結論。スワセン。
ベストプラクティスとは要はこういうことですよ。
class ItemData{
string ID { get; set; }
string Name { get; set; }
int Price { get; set; }
}
ItemData item = ParseLine(lines);
複数値があるものを返したかったらクラス作れと。後始末なんてガーベージコレクションにまかせて
どこで初期化するかなんて考えるなと。それは機械の役目であって人間の役目ではないと。
そういうことですな。
でもなんかコード量多くなってね?多くなっても意図がわかりやすいのであれば正しいコードなのかな・・・?
ラベル:C#