With文

| | コメント(8)

OOでプログラムを書く場合、同じオブジェクトに対して連続してメソッドを呼ぶことがよくあります。
短い変数名ならまだ良いのですが、GUIコントロールなどのメンバ変数だとだらだら同じ名前を書くのが面倒です。
補完を使えば入力は早くなりますが、コードとして冗長であるだけでも気持ち悪くなります。
コピペ移植性も下がりますし、可読性も下がります。


this._listHoge.Items.Add("あいうえお");
this._listHoge.Items.Add("かきくけこ");
this._listHoge.Items.Add("さしすせそ");

Smalltalkではこのような特定のレシーバに対するメッセージが連続する場合に、
レシーバを省略する構文糖衣が用意されています。
それがカスケードです。
これだと置換ミスが減りコピペ移植が楽になります。


listHoge items add: "あいうえお";
	 items add: "かきくけこ";
	 items add: "さしすせそ".

仮にC#でできたとしたらこんな感じ。


_listHoge.Items.Add("あいうえお"):
	 .Items.Add("あいうえお"):
	 .Items.Add("あいうえお");

VBにもカスケードによく似たWith文というものがあります。
With文では先頭で変数名を指定すると、End Withがくるまでは変数名を省略することができます。
Withの場合はSmalltalkのカスケードと違ってレシーバ以外も省略できるし、
途中で別の文が挟まっても使えるのでより使用範囲が広いと思います。
また、Withは入れ子にすることもできます。


With Me.listHoge
  .Items.Add "あいうえお"
  .Items.Add "かきくけこ"
  .Items.Add "さしすせそ"
End With

仮にC#でできたとしたらこんな感じ。


with (this._listHoge) {
  .Items.Add("あいうえお");
  .Items.Add("かきくけこ");
  .Items.Add("さしすせそ");
}

VBの言語仕様はかなり嫌いなのですが、このWithだけはかなり気に入っています。
だからC#にもぜひ取り入れて欲しかったのですが、残念ながら使えません。

C#でスマートに書ける方法はないものでしょうか。
usingはスコープ抜けるとDisposeが呼ばれるので用途違いますね。
ぬるり発生します。


using (ListBox lst = this._listHoge) {
  lst.Items.Add("あいうえお");
  lst.Items.Add("かきくけこ");
  lst.Items.Add("さしすせそ");
}

C#2.0で導入された匿名メソッドを使えばできるでしょうか。


delegate (ListBox lst) {
  lst.Items.Add("あいうえお");
  lst.Items.Add("かきくけこ");
  lst.Items.Add("さしすせそ");
} (this._listHoge);

コンパイルで怒られます。
関数オブジェクトとしてしか使えないんでしょうかねぇ。

要するにレキシカルスコープが作れりゃいいんですが…
と思ってたら単純な話でした。


{
  ListBox lst = this._listHoge;
  lst.Items.Add("あいうえお");
  lst.Items.Add("かきくけこ");
  lst.Items.Add("さしすせそ");
}

これだけで良かったのですね。
C#のレキシカルスコープってシンプルだけどなんかしっくりこないなぁ。

カスケードはいらないけどやっぱりWithはあった方が便利ですよ。
C#の言語設計者への要望だか質問だかにWith入れてくれってのがありましたが、
一時変数作ればいいじゃん、みたいな回答されてて相当萎えさせられます。
C#はけっこう好きなだけに。
With文はこの範囲ではこのオブジェクトが主役ですよ、
という意味的なメッセージとしての効果が高いんですよ。
単に記述を減らしたい以上のもので、一時変数云々なんてのは言語設計者として如何なものか…

コメント(8)

neri :

WithはVBの文法で唯一評価できる文法ですねぇ

一時変数に代入する方法は階層が深い時に参照回数が減ってパフォーマンスが向上する場合があるので使ってます

>WithはVBの文法で唯一評価できる文法ですねぇ

同意です。

私は基本会社でVB、家でRubyなのでC系やると間違いなくセミコロン付け忘れます。
あと関数適用の括弧書き忘れます…


>一時変数に代入する方法は階層が深い時に参照回数が減ってパフォーマンスが向上する場合があるので使ってます

なるほど。
Withも内部的には一時変数使ってますかね。
それとも展開されてしうまうのかな。

wiz :

VBのWithステートメントは高速化されることが明示的に書かれているので
内部では一時変数のような感じで処理してるかな。正確にはちょっと違うが。

ま、私は一時変数で良いと思う。ただ、一時変数の名前で迷ったりすることがあって
ちょっとアレかな。まぁ、Javaみたいに同名変数を宣言できないとかいう
トンデモ仕様じゃない限りあまり問題ないけど。

私的にはオブジェクト指向しかできないJava何かの考え方が微妙すぎてあれかな。
シングルトンみたいに再利用されないクラスがあったり、sprintfみたいに
クラスメンバに属させにくいものとかの扱いとか。Stringクラスを継承して実装するなんて嫌だし・・・

> 一時変数の名前で迷ったりすることがあって

これ重要ですよ。
そもそもスコープが狭ければ一時変数に名前なんていらないはず。
無名関数の必要性と少しかぶりますね。
「名前がない」ということ自体がある意味名前なわけですよ。
少なくとも適当に付けた数文字の変数名よりはずっと意味がこもってます。


> Javaみたいに同名変数を宣言できないとかいう

Javaのレキシカルスコープってどうやって作るの?
まさか無名クラス使うとか…

wiz :

普通にC++とかと同じで、
{
  String foo = bar.hoge.fuga.foo;
  foo += "...";
}
みたいな。
Withは2つ以上を省略できなくて微妙に不便なのよね。代入先と代入元が共に長い時
省略できるのが片方だけなんで・・・

> Withは2つ以上を省略できなくて微妙に不便なのよね。代入先と代入元が共に長い時
> 省略できるのが片方だけなんで・・・

2個省略できるなら欲しいけど1個だけならいらないってことですか?
一時変数を使うにしてもWithなら1個減りますよね。

あと2個以上省略できるような構文糖衣を用意することは可能ですが、本当にそれ欲しいですか?
下手に使われると著しく可読性下がりますよ?

wiz :

まぁようするに、

with 深い階層1 as a, 深い階層2 as b, 深い階層3 as c, ...
   for(...) a.array[i] = b.array[i]
   b.array[i] = c.data;
end with
みたいな。

要は一時変数でも済む罠。つ話。

1個だけ省略でもないよりましだけど不便なことが多いという話しです。大規模なものをきちんと組むとですが。
特に複雑なデータ構造を使うとなるとなかなかアレですな。

> まぁようするに、

その例のwithって単なるレキシカルバインドだよね?
複数オブジェクトの省略とは言えないと思います。
そんなのは言語仕様として持っていて当然。
VBにないのはVBが糞なだけ。


> 要は一時変数でも済む罠。つ話。

もちろん済むには済みますが、
便利なもんはあった方がいい罠という話。


> 1個だけ省略でもないよりましだけど不便なことが多いという話しです。

私はwith(オブジェクト省略表記)があればlet(レキシカルバインド)が
いらないって言ってるわけではありません。
withよりletの方がずっと重要だと思ってますよ。
ただOOPLの場合はletがあった上で更にwithがあればもっと便利だろうと…

このブログ記事について

このページは、yuchが2005年6月12日 23:08に書いたブログ記事です。

ひとつ前のブログ記事は「皐月」です。

次のブログ記事は「シーザー暗号 (1)」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 4.01