投稿数 - 437, コメント - 59536, トラックバック - 156

このプロパティはフィールドのラッパーかそうでないか

アクセサ以外のプロパティは必要か

引用
C#でプログラミングする場合、最近ではフィールドをカプセル化するためのアクセサにしかプロパティを使おうとしない自分がいる。

私も同じ。9 割以上のプロパティは、フィールドの単なるラッパーにしかなっていない。これを半ば常識としているので、稀にある「途轍もなく複雑な計算をしているプロパティ」や「データベースやファイルのような外部リソースにアクセスしているプロパティ」が極端なパフォーマンス劣化をもたらす場合がある。

Clazz d = new Clazz();
double sum = 0;
for(int i = 0; i < 10000; i++){
    sum += d.Value;
}

上記コードで、Clazz クラスには、Value プロパティが定義されている事が分かる(慣例に従い、フィールドは公開されているはずがないとする)。Value がフィールドの単なるラッパーだったら全く問題ないのだが、getter の実装がデータベースからデータを取得しているものだとしたら、一万回のデータベースへの接続が行われるため、パフォーマンス劣化は避けられない。当然、以下のように書けばいいじゃんって話になる。

Clazz d = new Clazz();
double sum = 0;
double value = d.Value;
for(int i = 0; i < 10000; i++){
    sum += value;
}

このように書くのは、Value プロパティの実装を「知っている」から出てくる発想かもしれないが、プロパティの実装を知り得ない以上、ループ内でのプロパティへのアクセスは避けるべきだろう。

「そのようなプロパティはプロパティではなくメソッドにすべきだ」という意見もあるだろう。至極もっともだ。そうした方がよい。コーディングルールに「複雑な処理はプロパティではなくメソッドにする」なんてのもありかもしれない。しかし、曖昧かつ無意味に強すぎるルールを全員が守っているかどうかは疑問だし、そもそも既存のライブラリにはその期待を持てない。従って、使う側が「このプロパティは凄く重い処理をしているかもしれない」という事は常に気に留めておくべきだろう。

投稿日時 : 2007年11月8日 14:03

フィードバック

# re: このプロパティはフィールドのラッパーかそうでないか

メソッドにした場合でも、結局このメソッドは軽いからループしてよい、
このメソッドは重いからループしてはいけない、という中身を知った上での判断が迫られるわけで。

古くはデータ構造による得手不得手を把握して処理を書く話に遡るのかな。
単方向リストとかそういう話。
2007/11/08 15:07 | 凪瀬

# re: このプロパティはフィールドのラッパーかそうでないか

>メソッドにした場合でも、結局このメソッドは軽いからループしてよい、
>このメソッドは重いからループしてはいけない、という中身を知った上での判断が迫られるわけで。

いや、リンク先にもあるように、メソッドには「何か処理する」という明確な意思があるので、ループ内で同じメソッドを何度も何度も呼ぶことは、必要がない限りは少ないでしょう。それに対し、プロパティは「まるで値」のように振舞うので意図せず「重い事」をしてしまうわけです。
2007/11/08 15:14 | 囚人

# re: このプロパティはフィールドのラッパーかそうでないか

なるほど。
Javaの場合はプロパティがいまんとこないからgetterでの取得になるからこの罠が回避しにくいわけだな。
というか、明示的にプロパティというものができたのなら、
そっちに重い処理を書くのはNG!とは確かに言えますね。
2007/11/08 16:03 | 凪瀬

# re: このプロパティはフィールドのラッパーかそうでないか

>コーディングルールに「複雑な処理はプロパティではなくメソッドにする」なんてのもありかもしれない。しかし、曖昧かつ無意味に強すぎるルールを全員が守っているかどうかは疑問だし

あははははははorz
でも、つけてた方がマシになってますよん。
プロパティでDB接続に行った上、結果の文字列を連結する、DQNなのが減ったので
2007/11/08 17:14 | Chuki

# re: このプロパティはフィールドのラッパーかそうでないか

私の場合,C# でプロパティを読み書きするときに,それがプロパティかフィールドかの違いはあまり意識してないです.
後で重いと分かったらもちろんプロパティは疑いますけど.

まあ「データベースやファイルのような外部リソースにアクセスしているプロパティ」といった極端な例に出くわしたことはないですが,Reflection で名前を取得していたり,アクセスするたびに巨大な (多数の) 参照型オブジェクトをばらまくようなプロパティについて言えば,多少用心して使うことは確かにあります.
string 型を返すプロパティ (特に内部で毎回リフレクションを使っていそうな場合),MethodInfo 型や FieldInfo 型を返すプロパティ (用途によってはハンドルで十分) あたりでしょうか.

あと,この例では d.Value が不変と仮定してますが,不変かどうか知っているということは事前に実装詳細をある程度知っていることになって,そのあたり設問としてはちょっと不自然かなという気もします.

また,最近 C# でも遅延評価が取り上げられるようになりましたが,関数型言語では遅延評価とセットでメモ化も駆使してたりしますからね.例えば「このオブジェクトは.NET Remotingによるリモートオブジェクトかもしれない」と考え出すと,メモ化できるところはひたすらメモ化してしまえということで,(可能なら) フィールドに対しても例と同じコーディング戦略を採るべしという話になる気がします.

私の方針についてのまとめ
・.NET では基本的にはプロパティかどうか気にしていない
 ・さらに物事を気にしたくないときは Haskell でも使う
・.NET でも,経験上いくつかのケースでは内部実装を気にする
・この件でワーストケースを想定するなら .NET Remoting
2007/11/08 17:17 | NyaRuRu

# re: このプロパティはフィールドのラッパーかそうでないか

>でも、つけてた方がマシになってますよん。

ありゃ。コーディングルールってあんまり好きじゃないんですよねぇ^^;
2007/11/08 17:51 | 囚人

# re: このプロパティはフィールドのラッパーかそうでないか

>あと,この例では d.Value が不変と仮定してますが,不変かどうか知っているということは事前に実装詳細をある程度知っていることになって,そのあたり設問としてはちょっと不自然かなという気もします.

確かにその点は書く前にちょっと引っかかった所ですが、「2度続けて呼び出す場合に異なる結果が返る事はない」というのはプロパティに期待して良い事かなと思います(マルチスレッドで且つロックしていない場合は除く)。そうならない場合はそれこそメソッドにすべき処理かなと思います。


>・.NET では基本的にはプロパティかどうか気にしていない

確かにそうです。なので、気にしなくて良いように基本「フィールドも同じ戦略」といきたいところです。


>・.NET でも,経験上いくつかのケースでは内部実装を気にする

はとても同意です。


>・この件でワーストケースを想定するなら .NET Remoting

あ、そうすればよかった^^;
2007/11/08 17:52 | 囚人

# re: このプロパティはフィールドのラッパーかそうでないか

> getter の実装がデータベースからデータを取得しているものだとしたら、一万回のデータベースへの接続が行われるため、パフォーマンス劣化は避けられない。

端から、プロパティとはそういうものだと思ってます。
ループの中で、プロパティ値を取得するのなら、あらかじめローカル変数に代入してますね。

DRYな人なので...
2007/11/08 21:16 | かずくん

# re: このプロパティはフィールドのラッパーかそうでないか

プロパティかメソッドかの選択のガイドラインがあります。
http://msdn2.microsoft.com/ja-jp/library/ms229054(VS.80).aspx

「操作がフィールド セットの場合よりも格段に低速の場合。」はメソッドにしろとなってます。
とても守ってほしいガイドラインですが、誰もが守ってくれるかどうかは…。
2007/11/08 21:37 | siokoshou

# re: このプロパティはフィールドのラッパーかそうでないか

>DRYな人なので...

私もです。


>「操作がフィールド セットの場合よりも格段に低速の場合。」はメソッドにしろとなってます。

おー、なるほど。納得ですねぇ。
2007/11/08 22:03 | 囚人

# staticにstaticに

staticにstaticに
2007/11/08 23:57 | 中の技術日誌ブログ

# re: このプロパティはフィールドのラッパーかそうでないか

はじめまして。

コントロールのプロパティへのショートカットをFormに追加することをよくやります。
仕事上はVB.Netなことが多いので、グリッドなどへのアクセスに重宝します。
Private Property FirstName(row As Int32) As String
Get
Return Grid.Cells(row,ColumnIndex.FirstName).Text
End Get
Setterは省略
End Property

Bindingを使えれば不要なんでしょうけど、なかなか使わせてもらえないので次善策。
2007/11/09 21:46 | dai-okuno

# re: このプロパティはフィールドのラッパーかそうでないか

>コントロールのプロパティへのショートカットをFormに追加することをよくやります。

確かにそれはやる事ありますね。
2007/11/10 12:55 | 囚人

コメントの投稿

タイトル
名前
URL
コメント