- - PR -
C# 特定のプロパティへの外部からのアクセスを隠蔽したい
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2004-12-06 15:01
これはどういう意味合いでうまく隠蔽できないと仰ってますか? クラスを継承する(という条件を崩さない)以上、(C++とかではちょっぴり違うこともありますが)基底のクラスで定義された公開機能を削除することは不可能です。 ですから、例えば private new なメソッドを定義してインターフェイス上から消す、などの方法しかありません。 現実には基底の型からはそのまま呼び出せますから、そういう意味合いで隠蔽できないという意味であれば、まあ仕方がないところですね。 例外を投げるというやり方なら(つまり実行時に確実に防ぎたいなら)、基底の方でメソッドをオーバーライドし、実体が派生クラスだったら例外を投げるという方法もあるでしょう。 この方法では確実に防げますが、インターフェイスからは消えません。private new なメソッドで隠蔽すれば直接のインターフェイスからも消すことはできますが、さらに基底の型から呼び出すことができるのでちょっと微妙ですね(やらないよりはやった方が良いかもしれませんが)。 --追記-- って書きましたが、単純に実体が派生クラスという判断をすると、基底クラスのコードから呼んでも駄目になりますね…やっぱりこの系統だと強引な方法になりそうな気がしてきました。 [ メッセージ編集済み 編集者: なちゃ 編集日時 2004-12-06 15:36 ] | ||||||||||||
|
投稿日時: 2004-12-06 15:53
tempはTextBoxが持つ特徴を引き継いでいるように思うのですが... | ||||||||||||
|
投稿日時: 2004-12-06 17:36
[Obsolete("使用禁止", true)]
public new .. でコンパイルエラーにするのはどうでしょう。 | ||||||||||||
|
投稿日時: 2004-12-06 19:37
private tempを作成するところまではわかりますが、tempが持つ特徴から必要な
ものだけをPinkTextBoxに引き継がせるエレガントな方法が分かりません。 例えば下記のようにしてひたすら全てのメンバー(BackColor以外)をひもづけるしか 無いという理解で正しいしょうか? 例 protected string Text { get { return temp.Text; } set { temp.Text = value; } } [quote] iStationさんの書き込み (2004-12-06 15:53) より: [quote] ひろしさんの書き込み (2004-12-06 14:21) より: ... temp = new TextBox(); ... private TextBox temp; ... [/quote] tempはTextBoxが持つ特徴を引き継いでいるように思うのですが... [/quote] | ||||||||||||
|
投稿日時: 2004-12-06 19:43
> 実はわたしも試みたのですが、単に"private new"を定義しても、
> うまく隠蔽できないような気がします。皆さんはどうでしょうか。 どういうことを試して、どういう結果が得られたので、『うまく隠蔽できない』とお考えですか? 私が試した結果では、「インテリセンスがうまく隠蔽していない」ように思います。または、「インテリセンスおよびコンパイラが拡大解釈して候補を一覧している」か。 以下のコードで試してみました。
このとき、button1_Clickメソッドの逆アセンブルの一部が、次のようになります。
注目は、 ・protectedアクセスに変更したTextプロパティに、Formクラスからアクセスできていること です。ところが、実行するとわかりますが、実際にはMyTextBox.Textプロパティ(のGetアクセサ)にはアクセスできていません。IL_0046を見ると、System.Windows.Forms.Control::get_Text()メソッドにアクセスしています。これが「拡大解釈して」の部分です。つまり、「this.Text = my.Text;」は、「this.Text = ((System.Windows.Forms.Control) my).Text;」に置き換えられているようです。 #ただ、デバッガで止めて「My.Text」をポイントすると、 #「MyTextBox.Textプロパティ」のアクセス結果が表示されます。 # これは、どうなんだろう? おそくてすまん。。。 _________________ | ||||||||||||
|
投稿日時: 2004-12-06 21:50
こんばんは、meiです。
ええ、tempへの委譲処理を書くことになります。 TextBoxのプロパティが多いですが、作っているアプリケーションですべてのプロパティを必要とする訳ではないので、実際はそれほど多くないと思います。 あと、質問なのですが、ここで言っている隠蔽とは、
このようなコードをコンパイルエラーにすることでしょうか? もし、これが狙いでしたら継承は使えません。 そうではなく、単に親クラス(TextBox)のプロパティを書き換えられたくないのでしたら、overrideして、set {...}の処理をつぶして、クラス内部からはbase.Textのように親クラスのプロパティを呼べは良いです。そのほかだと、xxxChangedイベントで強引に値を戻してやることも出来ます。(お勧めしませんけど・・・) どうしてもコンパイル時に隠蔽したいのなら、ラッパークラスを作ってひたすら委譲するしかありません。力技が好ましくない場合は、リフレクションを使って雛形を自動生成させたり、RealProxy/ContextBoundを使ってメソッド呼び出しに割り込むといった手も取れますが、実現したい内容に比べて処理が難しくになるのでお勧めしません。 http://www.ne.jp/asahi/nami/mei/cstips/adapter.html ↑昔遊びで作ったRealProxyのサンプルです。参考までにどうぞ。 [ メッセージ編集済み 編集者: mei 編集日時 2004-12-06 21:54 ] [ メッセージ編集済み 編集者: mei 編集日時 2004-12-06 22:01 ] | ||||||||||||
|
投稿日時: 2004-12-06 21:56
Jittaさん ご回答ありがとうございます。
> どういうことを試して、どういう結果が得られたので、『うまく隠蔽できない』とお考えですか? わたしが試みた内容です。 なぜYellowTextBoxの背景がピンク色に書き換えることができるか分かりません。 わたしはどこをどう勘違いしているのでしょうか? // // ***** Formの記述 ***** // (WindowsFormにtextBoxでテンプレートを作成し、YellowTextBoxに置換) // YellowTextBox textBox1; … this.textBox1 = new YellowTextBox(); … // // textBox1 // this.textBox1.Location = new System.Drawing.Point(56, 224); this.textBox1.Multiline = true; this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(496, 256); this.textBox1.TabIndex = 3; this.textBox1.Text = "textBox1"; // // 隠蔽したはずのBackColorにすんなり代入できてしまう! // (実際、画面にピンク色の背景が表示されてしまう) // ↓ this.textBox1.BackColor = System.Drawing.Color.Pink; … // // ***** YellowTextBoxの記述 ***** // public class YellowTextBox : System.Windows.Forms.TextBox { public YellowTextBox() { base.BackColor = yellow; } // // BackColorをprivate宣言して隠蔽を試みる // private new System.Drawing.Color BackColor { get { return base.BackColor; } } private readonly System.Drawing.Color yellow = System.Drawing.Color.Yellow; } [quote] Jittaさんの書き込み (2004-12-06 19:43) より: > 実はわたしも試みたのですが、単に"private new"を定義しても、 > うまく隠蔽できないような気がします。皆さんはどうでしょうか。 どういうことを試して、どういう結果が得られたので、『うまく隠蔽できない』とお考えですか? 私が試した結果では、「インテリセンスがうまく隠蔽していない」ように思います。または、「インテリセンスおよびコンパイラが拡大解釈して候補を一覧している」か。 以下のコードで試してみました。 [code] public class MyTextBox : System.Windows.Forms.TextBox { public MyTextBox() { } protected new string Text { get { return "+" + base.Text; } set { base.Text = value; } } public string GetText() { return this.Text; } } 〜〜〜〜〜 private void button1_Click(object sender, System.EventArgs e) { foreach (System.Windows.Forms.Control cntrl in this.Controls) { if (cntrl.Name.CompareTo("myTextBox1") == 0) { MyTextBox my = (MyTextBox) cntrl; this.label1.Text = my.GetText(); this.Text = my.Text; break; } } } [/code] このとき、button1_Clickメソッドの逆アセンブルの一部が、次のようになります。 [code] .try { IL_000c: br.s IL_0052 IL_000e: ldloc.2 IL_000f: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() IL_0014: castclass [System.Windows.Forms]System.Windows.Forms.Control IL_0019: stloc.0 IL_001a: ldloc.0 IL_001b: callvirt instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Name() IL_0020: ldstr "myTextBox1" IL_0025: callvirt instance int32 [mscorlib]System.String::CompareTo(string) IL_002a: brtrue.s IL_0052 IL_002c: ldloc.0 IL_002d: castclass WindowsApplication1.MyTextBox IL_0032: stloc.1 IL_0033: ldarg.0 IL_0034: ldfld class [System.Windows.Forms]System.Windows.Forms.Label WindowsApplication1.Form1::label1 IL_0039: ldloc.1 IL_003a: callvirt instance string WindowsApplication1.MyTextBox::GetText() IL_003f: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string) IL_0044: ldarg.0 IL_0045: ldloc.1 IL_0046: callvirt instance string [System.Windows.Forms]System.Windows.Forms.Control::get_Text() IL_004b: callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Text(string) IL_0050: br.s IL_005a IL_0052: ldloc.2 IL_0053: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0058: brtrue.s IL_000e IL_005a: leave.s IL_006d } // end .try [/code] 注目は、 ・protectedアクセスに変更したTextプロパティに、Formクラスからアクセスできていること です。ところが、実行するとわかりますが、実際にはMyTextBox.Textプロパティ(のGetアクセサ)にはアクセスできていません。IL_0046を見ると、System.Windows.Forms.Control::get_Text()メソッドにアクセスしています。これが「拡大解釈して」の部分です。つまり、「this.Text = my.Text;」は、「this.Text = ((System.Windows.Forms.Control) my).Text;」に置き換えられているようです。 #ただ、デバッガで止めて「My.Text」をポイントすると、 #「MyTextBox.Textプロパティ」のアクセス結果が表示されます。 # これは、どうなんだろう? おそくてすまん。。。 [/quote] | ||||||||||||
|
投稿日時: 2004-12-06 22:29
つまり、私がやったことと一緒ですね。なちゃさんやmeiさんが書かれているとおりで、継承によってpublicで公開されたメソッドを隠蔽、見えなくすることはできません。ん〜っと、publicで宣言されたメソッドを、private、publicで宣言し直すことはできません。暗黙的に、継承元のクラスにキャストされます。私は元々C++なので、C++的には「おかしい」のですが、C#やVB.NETでは仕様のようです。 したがって、 this.textBox1.BackColor = System.Drawing.Color.Pink; このコードは、 ((System.Windows.Forms.TextBox)this.textBox1).BackColor = Color.Pink; として解釈され、またILにもそう訳されます。第一、このコードがコンパイルエラーにならないのがおかしいと思いませんか? ユーザ入力を受け付けなくするには、public overrideで継承して、setアクセスメソッド内で握りつぶすしかないようです。
_________________ |