トップ回答者
イベント定義(C++/CLI)

質問
-
お世話になっております。
VS2008を使用しております。
delegate void MethodPrint(void);
void Print(){System::Console::WriteLine("Call Print")};ref class Test
{
public:
event MethodPrint^ OnClick;
};main()
{
Test^ obj += gcnew MethodPrint(Print);
obj->OnClick();// コンパイルエラー
}エラー 1 error C3767: 'Test::OnClick::raise': 候補の関数はアクセス可能ではありません。
確かにildasmでみてえみると、hidebysig がつけられ隠蔽されていました。
これはなぜなのでしょうか?
回答
-
残念ながらこういった情報源(設計思想的な情報など)は英語である場合がほとんどです。今回は意訳しますが、一文ずつ、少しづつ翻訳していくなど慣れてくださいね。また、理解の前提としてC#やILといった知識が必要です。
概要のみ意訳です。
私の理解している範囲でですが、C++/CLIを除くほとんどのマネージ言語は、アプリケーションのILに.fireメソッドを用意しません。その結果、.fireメソッドは暗黙のうちにprivateになります。そのため、そのイベントを実行(呼び出し)できるのは、イベントを用意したクラスだけです。そのクラスの子クラスはprivateのため実行(呼び出し)できません。従って、イベントを実行(呼び出し)できるようにする為に、protectedのOnEventメソッドをイベントごとに用意する必要があります。そうすれば、子クラスからもイベントを実行(呼び出し)できるようになります。
C++/CLIでは.fireメソッドを用意します。raiseメソッドをoverrideしなかった場合、イベントがpublicもしくはprotectedだったら、raiseメソッドはprotectedになります。イベントがprivateなら、raiseメソッドはprivateになります。従って多くの状況において、OnEventを用意しなくてもよくなります。
すべての返信
-
>これはなぜなのでしょうか?
仕様です。但し、OverrideすればAccessできます。
以下のパターンを利用します。
delegate void MethodPrint(void); void Print(){System::Console::WriteLine("Call Print");} void Print2(){System::Console::WriteLine("Call Print 2");} ref class Test { public: event MethodPrint^ Click { void add(MethodPrint ^methodPrint) { delMethodPrint = (MethodPrint^)Delegate::Combine(delMethodPrint,methodPrint); } void remove(MethodPrint ^methodPrint) { delMethodPrint = (MethodPrint^)Delegate::Remove(delMethodPrint,methodPrint); } void raise() { delMethodPrint->Invoke(); } } protected: MethodPrint ^delMethodPrint; }; ref class Test2 { public: event MethodPrint^ Click; void OnClick() { Click(); } }; int main(array<System::String ^> ^args) { Test ^obj = gcnew Test(); obj->Click += gcnew MethodPrint(&Print); obj->Click(); Test2 ^obj2 = gcnew Test2(); obj2->Click += gcnew MethodPrint(&Print2); obj2->OnClick(); return 0; }
細かいError Checkは省いています。Test2の使い方がよく使われます。
-
ご返事ありがとうございます。
dotNET的考え方ととらえるべきでしょうか。
いつもは、Test2クラスと同じ実装で特に意識してなかったのですが、
Testクラスと同じようにeventの展開をオーバーライドする形で実装したときに、
あれ、直接呼べるけどいいのかなと疑問をもち、下記のよう隠蔽すべきなのかなと思ったのですが、
なぜ、コンパイラはhidebysig をわざわざつけているのかなと疑問に思い質問させていただきました。
dotNETの仕様を踏襲する場合は、下記のようにするべきでしょうか。event MethodPrint^ Click
{
private:
void raise()
{
}
}
-
.NET的というより、C#的、IL的という表現のが近いでしょう。Managed Languageの中心はC#であるイメージがあります。
>なぜ、コンパイラはhidebysig をわざわざつけているのかな
以下が参考になります。
>下記のようにするべきでしょうか。
制御子はAssemblyやClassの要件に応じて使い分けですね。public以外の制御子にして、OnXXX経由で統一したほうがよいと個人的に思います。
[Member Visibility]
http://msdn.microsoft.com/en-us/library/45x418az(v=VS.90).aspx
-
残念ながらこういった情報源(設計思想的な情報など)は英語である場合がほとんどです。今回は意訳しますが、一文ずつ、少しづつ翻訳していくなど慣れてくださいね。また、理解の前提としてC#やILといった知識が必要です。
概要のみ意訳です。
私の理解している範囲でですが、C++/CLIを除くほとんどのマネージ言語は、アプリケーションのILに.fireメソッドを用意しません。その結果、.fireメソッドは暗黙のうちにprivateになります。そのため、そのイベントを実行(呼び出し)できるのは、イベントを用意したクラスだけです。そのクラスの子クラスはprivateのため実行(呼び出し)できません。従って、イベントを実行(呼び出し)できるようにする為に、protectedのOnEventメソッドをイベントごとに用意する必要があります。そうすれば、子クラスからもイベントを実行(呼び出し)できるようになります。
C++/CLIでは.fireメソッドを用意します。raiseメソッドをoverrideしなかった場合、イベントがpublicもしくはprotectedだったら、raiseメソッドはprotectedになります。イベントがprivateなら、raiseメソッドはprivateになります。従って多くの状況において、OnEventを用意しなくてもよくなります。