青の部隊 505小隊 ULZ

三日目

最終更新:

Bot(ページ名リンク)

- view
だれでも歓迎! 編集
電卓を作ろうとボタンとテキストボックスを配置しました。
こっからどうすればいいのか。

謎のコード

生成されたコードをよくよく見てみると、MainFormクラス(初期のForm1クラスじゃかっこ悪いので変更しました)のコードがMainForm.csとMainForm.Designer.csの二つに分かれているようです。こんなことできるの?
さらによく見ると、クラス宣言にpartialとか言う謎のトークンがある。不完全っていう意味だっけ?ふむ、ちょっと予想がつく。ググってみよう。


Partial Type
C# 2.0 では、クラスや構造体などの型を複数のソースファイルに分けて記述できる
ようになりました。分けて記述したい型には、以下のように partial キーワードを
付けます。 

public partial class Customer
{
  private int id;
  private string name;
  private string address;
  private List<Order> orders;
  public Customer() { ... }
}
// ↑のクラスと↓のクラスは別ファイルに記述可能。
public partial class Customer
{
  public void SubmitOrder(Order order) { orders.Add(order); }
  public bool HasOutstandingOrders() { return orders.Count > 0; }
}

この2つのクラスを記述したファイルを一緒にコンパイルすることで、1つのクラスに結合
することが出来ます。クラスの結合はコンパイル時に行う(DLL 参照時にはできない)ので、
Partial Type の全ての部分を一緒にしてコンパイルする必要があります。

なるほど。しかも生成されたコードは、
partial class MainForm

public partial class MainForm : Form
と、宣言がなんか違う。一箇所だけ詳しく書いとけば、ほかは省略していいってことかな。まあめんどいから、必要になったら検証しよう。
2点違いますね。一個一個解説。

publicのありなし

基本的にclassのアクセス修飾子は省略するとinternal(そのプロジェクト内でのみ参照可)になります。
ただし、partialで分裂させた場合は、どれか一つにアクセス修飾子を書いておけばそいつにアクセス制限がゆだねられます。
なのでこの場合、MainFormクラスははpublicで宣言されていることになります。
裏側(MainForm.Designer.cs)に書いてないのは、MainFormクラスのアクセス修飾子をユーザーが決められるようにしているためだと思います。

Form(継承)ありなし

partialクラスはコンパイル時に結合されるので、どちらか一方に継承関係を書いておけばOKです。
例えば、次のようなpartialクラスを新たに宣言してもOKです。
partial class MainForm : Form
ただし、C#は多重継承できませんので、次のようにした場合はコンパイルエラーとなります。
partial class MainForm : Control
(NZ-000)

ほほう。検証の必要がなくなりました。グッジョブです。サンクス。
C#の文法解説ページかけるんじゃね?
(pun)

どっちでもプライベートメンバにアクセスできる

ということは当然、MainForm.Designer.csで宣言されたプライベートメンバtextBox1に、MainForm.csからでもアクセスできるということですな。
というわけで、インテリセンスを駆使して超適当にコードを書いてみました。動くかな。

        private void Button_Click(object sender, EventArgs e)
       {
            textBox1.Text += "1";
       }

……ビルドできた!ボタンを適当に押してみると……

[図2]

うごいてる!カンで使えるとは……悔しいですがすばらしい言語です。

テキストボックスと文字列操作

上の適当なコードが動いてしまったので、後付けでテキストと文字列について書きます。
textBox1のメンバでSetText()がないか探してみたけど見当たらない。さらに探すと、Textメンバの説明に設定や取得とあるので、直接操作してみる。直接代入ではつまらないので、Javaと同じように+演算子で結合ができるのではないか、しかも+=とか使えちゃうのではないか、と思ってやってみたら動いた。という次第。
C#の文字列連結は他の言語(CLRを使わないもの)と比べてコストが高い。・・・たぶん。
それは連結のたびにオブジェクトの生成と廃棄が行われるから。
具体的には、
textBox1.Text += ":";
この一行の中で「Textプロパティの文字列+":"」分の新たなメモリ領域が確保され、
Textプロパティがそこを参照するようになります。
そして元の文字列が廃棄(このメモリの廃棄はこのとき行われるのかGCのタイミングで行われるのかはちょっと知らない)されます。
なので、文字列連結を多用する場合はStringBuilderを使いましょう。
(NZ-000)

これをみていろいろ考えたんだけど、StringBuilderって文字列を取得する方法がToStringしかなくて、
TextBoxに連結後の文字列を表示させるには、結局新たなstringを作成するしかないように見えるのね。
そうなってくると、+演算子の場合は
①"1" ← ①表示中
①"1"+②"2"=③"12" ← ①と②は破棄、③を表示
③"12"+④"3"=⑤"123" ← ③と④は破棄、⑤を表示
となり、StringBufferの場合は、
①"1" ← ①はStringBufferに保存、かつ、コピーとなるstringを表示中
①"1"+②"2"=③"12"+④"12" ← ②破棄、④を表示、③はStringBuffer、表示中だった①コピーは破棄
③"12"+⑤"3"=⑥"123"+⑦"123" ← ⑤破棄、⑦表示、⑥はStringBuffer、表示中だった④は破棄
という具合になって、どっちも表示のたびに二つのstringが破棄されて変わらないような気がするんだよ。
てか、「文字列連結を多用する場合」というのは、今回の場合ではなくて、
Textファイルの複雑な整形プログラムみたいなものを指してる?うーむ。
(pun)

うん、そう。誤解与えてすまそ。
(NZ-000)

ふむふむ。ついでに、文字列リテラルの扱われ方の記述を(正式じゃないけど)見つけたので……
と思ったけどJAVAだったので削除。

押されたボタンの判定

同じメソッドを複数のオブジェクトが使用するというコードになる以上、どのボタンが押されたか判定する手段があるはず。なら常識的に考えて、引数で判定するだろう。
        private void Button_Click(object sender, EventArgs e)
       {
            textBox1.Text = sender.ToString();
            textBox1.Text += ":";
            textBox1.Text += e.ToString();
       }
とりあえずこんなコードを書き、senderとeが何なのか調べてみます。結果、こんなものが表示されました。
System.Windows.Forms.Button, Text: 7:System.Windows.Forms.MouseEventArgs
しまった、コロンがふたつある……。でもどっちにしても、こんな文字列になるのなら、ToString()は分岐には使いにくいな。
というか、この結果から察するに、senderにはボタンオブジェクトそのものが入っているご様子。なら次のようなコードでいいのでは?
        private void Button_Click(object sender, EventArgs e)
       {
           if (sender == Number0)
           {
               textBox1.Text += "1";
           }            
       }
[図3]

かんぺきっす。ちゃんと0を押したときだけ動きます。
ほんとはswitch使いたいんだけど、まあいいか。……いや使えるか?
        private void Button_Click(object sender, EventArgs e)
       {
           switch (sender)
           {
               case Number0:
                   textBox1.Text += "0:";
                   break;
               case Number1:
                   textBox1.Text += "1:";
               case Number2:
                   textBox1.Text += "2:";
               default:
                   textBox1.Text += "x:";
           }
       }
……ビルドエラー、「整数型の値が必要です」とのこと。うーむ。GetHashCode()しか整数を返すメソッドはないか。ハッシュだと別のオブジェクトが同じコード返す可能性もあるし、使えないな……。普通はobjectをButtonにキャストしたりするんだろうか。今日のところはあきらめよう。ifのラダーで書くことにします。(コード省略)
整数というか定数でなければなりません。
なので文字列は可です。
変数は無理っす。
あ、コメントうざかったらどっかによけるか消してもらってもいいよ。
(NZ-000)
ほうほう。constオブジェクトもいける?あーでもコントロールをconstにはできんか……。
なんかここがスマートじゃないんだよなあ。
コメントはありがたいです。調べる手間がおもいっきし省けるんで。
そのうちまたまとめないといかんなあ。
(pun)

今日の成果

ついでに、テキストボックスのプロパティを触って、TextAlignをLeftからRightに変更しました。

[図]

今日はここまで。

まとめ

クラス宣言にpartialキーワードをつけると、クラスのコードを複数ファイルに分割できる。命名規則などは不明。partialは不完全という意味ではなくて、partの活用形で、部分的なとかそういう意味な気がする。

文字列は+で結合できそう。+=演算子も使える。

TextBoxコントロールの中の文字列は(TextBox).Textを操作すれば書ける(し、よめるだろう)。

ボタンクリックに関連付けられるハンドラの第一引数は押されたボタンそのもの。

オブジェクトの比較には==比較演算子を使用できる。

switch文は整数型しか使えない(ようだ、でも最近の言語の傾向からして、文字列くらい使えるかも……)。objectそのものを使うことは不可能。

タグ:

+ タグ編集
  • タグ:
ウィキ募集バナー