- PR -

特集「私がJavaからC#に乗り換えた10の理由」について

投稿者投稿内容
Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2003-07-17 13:00
MSDNのデリゲートのチュートリアルにインターフェイスとデリゲートの使い分けについての記述がありました。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/csref/html/vcwlkDelegatesTutorial.asp

私には、「デリゲートも使える場面」と「インターフェイスでないと駄目な場面」の区別に思うのですが…。
やはりデリゲートは、インターフェイスの機能を制限付きで簡便に扱うための機能なのでしょうか?

樋口/@IT
@ITスタッフ
会議室デビュー日: 2001/07/26
投稿数: 293
お住まい・勤務地: 東京都
投稿日時: 2003-07-17 14:13
@ITの樋口です。活発な議論、ありがとうございます>みなさん。

引用:

Jittaさんの書き込み (2003-07-17 11:00) より:

で、ここの「規約」を調べたのですが、会議室への投稿は、
すべて@ITの「著作物」になるようです。


これですが「ここに書き込まれた内容を元にして、どこかで本を出版したり、よそのWebサイトのコンテンツを生成したりしないでください」という主旨とお考えください。
ですので、
引用:

仮に議事録なり要約なりを作ったとして、それって、どうやって配る?見せる?


この会議室にフィードバックしていただくのがよろしいかと思います。と申しますか、大歓迎です(やってくれ、と圧力をかけているわけではありません )。

_________________
樋口 理
株式会社アットマーク・アイティ
ほむら
ぬし
会議室デビュー日: 2003/02/28
投稿数: 583
お住まい・勤務地: 東京都
投稿日時: 2003-07-17 14:50
ども、ほむらです。
まだまだ、調べるべきことは
ありそうな感じです。
勉強不足が身にしみます^^;;;;;;
-------------------
都合により改行位置を変えさせていただきました
ご容赦><

お犬様 氏へ
> 使用用途が違いますが Java にもリフレクションのために使用する
> java.lang.reflect.Method がありますし、
> これをオブジェクトへの参照と組み合わせて使えば
> 機能的には delegate と似たような事をする事も可能です。
> (コンパイル時の型検査が甘くなるか、コンパイル時の型検査をするために
> 結構な量のコードを追加するかしないといけないので普通はしませんが)
Javaでも、出来たんですね。^^;;;;
参照型はクラスに対してでしかできないものとばかり思っていました。
ちなみに、僕のJavaに対する認識は
C言語をベースにして
1.オブジェクト指向の導入(より厳密にカプセル化)
2.可読性を低下させる要因とされているgoto文の削除 (個人的にはgoto文大好きです(笑 )
3.プログラマにメモリ管理を意識させない。
4.同じくメモリリークの原因となるポインタの削除
なので、プリミティブな型(int等)同様 参照型も苦し紛れに
導入されたものとばかり(ぉぃ

java.lang.reflect.Method 。。。実装は難しそうなので
必要に迫られたときに改めて調べてみようと思います(激汗)

ya 氏へ
> 関数ポインタとつかい方及び動作がそっくりなので
> 関数ポインタと説明すると楽なんですが、
> 根本的に(概念は)「違うもの」です。
> 関数ポインタは関数への参照そのものですが、
> delegateは関数への通知(リダイレクト)を
> 実現するオブジェクト指向に乗っ取った型を持った(定義できる)オブジェクトです。
そうですね。
最初は文法をちょこっと変えただけかと思っていたのですが
〜〜〜〜〜
C と C# で書き比べてみるとこんな感じになるのでしょうか?
 C#:
  public delegate ObjectMoveDelegate(long delay, long type)
  public ObjectMoveDelegate d;
  d = new ObjectMoveDelegate( ObjectMoveFunc );
 C :typedef (*ObjectMoveDelegate)(long delay, long type)
  ObjectMoveDelegate d;
  d = (ObjectMoveDelegate)(ObjectMoveFunc);
(以下の3.の追加削除を行おうと思ったらSTLのvectorが必要になるのかな?)
〜〜〜〜〜
関数のポインタと比較してみると以下の点で
より高度なものになっているみたいですね。
1.一度登録したデリゲートは書き換えが出来ない
2.静的でないメソッドが登録可能
3.配列のように追加・削除ができる(チェーン?)

個人的に気になるのは2.の部分ですね〜
別々のインスタンスの同じクラス同じメソッドを登録した場合
メンバ変数の値は別に別になるんですかね?
これが安全に出来たらかなり便利な予感^^

> C++で実現するならclassを使い、
> 関数ポインタはそのオブジェクトの中でのみ使用されるものとなります。
理解できなかったので無視してしまおうかとも思ったのですが。。。。
C++から離れられていない僕としては気になるので
もう少し詳細を教えてもらえると助かります
nak2k
ベテラン
会議室デビュー日: 2003/07/17
投稿数: 86
投稿日時: 2003-07-17 15:11
活発な議論に思わず、メンバ登録させていただきましたK-NAKと申します。

引用:

ほむらさんの書き込み (2003-07-17 14:50) より:
1.一度登録したデリゲートは書き換えが出来ない
2.静的でないメソッドが登録可能
3.配列のように追加・削除ができる(チェーン?)

個人的に気になるのは2.の部分ですね〜
別々のインスタンスの同じクラス同じメソッドを登録した場合
メンバ変数の値は別に別になるんですかね?
これが安全に出来たらかなり便利な予感^^



以下のようなコードで、別々のインスタンスに対してきちんと呼ばれることを確認しました。
デリゲートインスタンスは、メソッドへのポインタに加えて、インスタンスへの参照も
保持しているようですね。
(私はむしろstaticメソッドへのデリゲートがどうなってるのかが不思議です^^;)

コード:
using System;

class App {
	delegate void DelegateTypeA();
	
	class A {
		int field;
		
		public A(int a) { field = a; }
		
		public void Print() {
			Console.WriteLine("filed = {0}", field);
		}
	}
	
	public static void Main() {
		A a = new A(2);
		A b = new A(3);
		
		DelegateTypeA d1 = new DelegateTypeA(a.Print);
		DelegateTypeA d2 = new DelegateTypeA(b.Print);
		
		d1();
		d2();
	}
}

いのつち
ベテラン
会議室デビュー日: 2002/05/14
投稿数: 73
投稿日時: 2003-07-17 15:24
引用:

Wataさんの書き込み (2003-07-17 13:00) より:

私には、「デリゲートも使える場面」と「インターフェイスでないと駄目な場面」の区別に思うのですが…。
やはりデリゲートは、インターフェイスの機能を制限付きで簡便に扱うための機能なのでしょうか?




「インターフェイスを使えば実現できる。(またはすべきである)」
と、
(delegateがなければ)
「インターフェイスを定義して複数のクラスに分けて実装せざる得ない。」

の違いで後者の場合にdelegateを採用するという
使い分けもありかもしれません。

設計の観点からクラスの粒度は適切でクラスを分割する必要性はない
(または分割すると煩雑になる・分けたところで再利用性は低い)場合、
delegate を使用する方がシンプルに収まると思います。

ライブラリ使用者側から見れば、モデルのイメージが直感的に伝わりやすい
のであれば、多くのクラス・インターフェイスより、
ひとつに収まっている方が、理解しやすいですし。

同じことを実現するために、複数の手段があるのは可読性を落とし、見通しが
悪くなる と仰るかもしれませんが。。

一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2003-07-17 15:26
Javaの無名インナークラスについて、分かったような気がします。
イベントを知らせる相手として、(インターフェイスを実装するなどして)ある決まったサービスを提供しているクラス(リスナー)を設定しておくということです・・・よね?
これはC#ではメソッドを登録するという部分ですね。Javaではクラスを登録すると。

Javaでは、「さぁ、ボタンClickよイラッシャイ」と、メッセージを受ける構えがあるところ(リスナー)にしかメッセージを送れなくて、リスナーのメソッド内で、他のオブジェクトのメソッドを呼んだり(これが他オブジェクトへのメッセージ?)するんですよね。・・・ですよね?
対してC#では、引数と返り値さえ同じであればどこへでもメッセージを送ってやろうということでしょうね。

さて・・・どうやって話を続けたものか。
どちらが良いというより、別の話のような気がしてきましたが。

とりあえずイベントの話は終わります。次にdelegateに関してですが、
引用:
>object さん
私は「delegate」が実際に「class」に変換されているから型そのものだ、というのは少し弱いと思います。
それは、「delegate」が型(Class)である事を確認しただけだと思います。

問題は、
何故「delegate」が型でないといけないか
に対する納得だと私は思いますよ?


「なぜ型でないといけないのか」ですが、私はdelegateはメソッドへのポインタを生の値ではなくオブジェクトとして扱うためのものだと思います。
たとえば、あるメソッドへのポインタをメモリー上のアドレス値でlong型などに入れて使おうとすると、どのインスタンスのメソッドなのか分かりませんし、足したり引いたりもできてしまいます。
それを、どのインスタンスのメソッドなのかという情報も保持し、「メソッドを呼び出す」というサービスしか提供しないことによって、メソッドへのポインタとして扱えるし、それ以外には扱えないわけです。
で、その機能を使って実装したのがイベントというわけ。

.NET的には、イベントというのは本当はdelegateではなくadd,removeアクセッサというものの事なんですが
参考:http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=3497&forum=7

[ メッセージ編集済み 編集者: 一郎 編集日時 2003-07-17 15:31 ]
ya
大ベテラン
会議室デビュー日: 2002/05/03
投稿数: 212
投稿日時: 2003-07-17 16:42
突っ込み感謝です。

>>一郎さん
引用:

問題となっているのは、メソッドの引数と返り値が同じであれば、作成者が意図していない(リンク先の言葉で言う継承されていない)型のデリゲートの値とすることもできるという事です。
それで、前の私の書き込みの
メソッドに明確な型を持たせるというのも面白いかもしれませんね
となるわけです。




この話ですが、classの中の出来事ですので、

コード:
c1.SampleEvent1 += new SampleEvent1Handler(m.listenClass2SampleEvent2);
c2.SampleEvent2 += new SampleEvent2Handler(m.listenClass1SampleEvent1);




こんなコード書いた時点でこのクラス(MainClass)設計者はm.listenClass2SampleEvent2メソッドに関連付けられたdelegateをc1.SampleEvent1へ与えている事になるんで、「意図していない」って言うのはおかしいです。c1.listenClass2SampleEvent2メソッドはprivateなんでクラス設計者の責任の及ばないクラス外からはメソッドにはアクセスできないわけですし(というかそのためのprivateとカプセル化)。
Class1とClass2の作成者はdelegateを参照して選択してその型の制限にしたがって実装すればよい(またはその責任さえ果たせばよい)し、利用者であるMainClassの作成者はdelegate型を参照してその制限に従って(その委譲用に)実装したメソッドを渡せばよい(またはその責任を果たせばよい)わけです。
利用するdelegate型はClass1設計者やClass2設計者が選択できますし、自分でも定義できますのでパラメータや戻り値以外にもAttributeとかを付加することができます。
そしてイベントに定義された型を強要されるのは利用者でそのdelegate型とことなった物を利用する事はできません。ただ利用者はその制限内で自分からアクセスしうるメソッドを登録する事ができるということです。
ちなみにおっしゃっているように、使用できるdelegateを制限すればクラス(MainClass)内でのミスも減らすことはできるかもしれませんが、そんなミスはしないでください(w。Class内はいくらなんでも自分の責任なんで…。

引用:

「同じ型から派生したものだと考える事ができる」というのは納得できませんでしたが。




えっとですね。私が考えたのは
void Method()型−継承→void Class1.SampleMethod1()
          −継承→void Class1.SampleMethod2()
という考え方です。共通化して扱いたい場合
((void Method())i.SampleMethod1)()
というようになります。つまり、「Class1.SampleMethod1()という「型」がある→これはvoid Method()型から継承したものだ」とすることによる実現です。
たぶん、この部分ですれ違っていて
void Method()型−インスタンス化→void i.SampleMethod1()
          −インスタンス化→void i.SampleMethod2()
と認識されているのかな、と思いました。私は前者のほうが自然だと思うのですが、この議論は別の方向に行きそうなので止めておきましょう。間違ってたらごめんなさい。


>>objectさん
引用:

私は「delegate」が実際に「class」に変換されているから型そのものだ、というのは少し弱いと思います。
それは、「delegate」が型(Class)である事を確認しただけだと思います。

問題は、
何故「delegate」が型でないといけないか
に対する納得だと私は思いますよ?




その理由として前回の私の書き込みは参考にならないでしょうか。delegateはclassなんですがなんかどうもclassじゃないっぽいところもあるような感じです(でも「型」です)。


>>Wataさん
・静的メソッドを使用して指定の実装を行う場合
・イベントに似たデザインパターンで作成する場合
・メソッドが定義されているオブジェクトが、呼び出し元と無関係の場合
この辺、インターフェイスでは難しい、またはdelegateの方が優れた実装ができるかな、と思ったり。


>>英-Ranさん
「理解していない」とおっしゃるのなら今後のためにできればご教授願います。
自分が間違っているのならそれを正したいし、また違う側面があるならそれを学習したいです。


>>ほむらさん
考えてみると、C++だと中途半端な実装しかできそうにないですねぇ…しかもかなりめんどい(一番の問題は普通の方法ではメソッドの関数ポインタがないこと)。
ただ、意味的には

コード:
class Delegate {
 private:
  //メソッドへの参照(ここではインスタンスのメソッドもOKっていうふうにしてください)
  void* m_RefObjectMethod;
 public:
  Delegate(void* RefObjectMethod) {
   m_RefObjectMethod = RefObjectMethod;
  }

  void Invoke() {
   //関数ポインタを呼ぶコード
  }
};




みたいなものをつくってこれ「だけ」を別のオブジェクトに渡します。
これだと意味があまりないように見えますがクラスであり、オブジェクトであるって言うのが重要なんです。
つかこれは、少し時間ください(w。
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2003-07-17 17:45
引用:
>>ya さん
void Method()型−継承→void Class1.SampleMethod1()
          −継承→void Class1.SampleMethod2()
という考え方です。


私は、
void Method()型−継承→SampleEvent1Handler−インスタンス→SampleMethod1()
          −継承→SampleEvent2Handler−インスタンス→SampleMethod2()
というイメージでした(正しい表現ではありませんが便宜上)。
コード:
class C{

private int i;
public void SampleMethod1(){return;}
}


というクラスがある場合、i型があると言わないように、SampleMethod1()型があるわけでもないと思うんです。
SampleMethod1()がインスタンスといっても、もちろんC型のインスタンスができた時に一緒にできるだけの話ですが(iのように)。

そうすると「−インスタンス→」の部分の型付けが弱いですよね。
SampleMethod1()はSampleEvent1Handler型と明示しているわけではないので、SampleEvent2Handler型としても扱えます。
で、前の「共通点を見つける」という話になるわけです。
つまり「引数と返り値自体がメソッド型の指定なんだ」ということ。


・・・・・・わけわかんなくなってきた。
私が何を主張したいのかも分からなくなってきました。
というより初めから主張したいことなんてありませんでした。
ちょっと休憩します。

[ メッセージ編集済み 編集者: 一郎 編集日時 2003-07-17 17:48 ]

スキルアップ/キャリアアップ(JOB@IT)