System.out.println("またJavaの季節がやってきた!" + 2);
■ このスレッドは過去ログ倉庫に格納されています
去年の今頃Java学習開始するも挫折
しかし不屈の闘志をめらめらと燃やしながら
そびえ立つ岩壁にいどむため、再びこの地にやってきたのだ!
プログラミング歴は独学Cチョビッツ+独学VBA少々
きょうかしょ
https://www.amazon.co.jp/dp/484433638X
スッキリわかるJava入門 第2版 (スッキリシリーズ) 2進数というスイッチカチャカチャの世界を、人が常用する10進数に重ねていくこの作業は見事だ
コンピュータが作られていく過程で試行錯誤を重ねてこういうものを作ってきた人たちがいたんだなと まだテキストで勉強してない分野をJavaと同じように書いてどこまでいけるか試してた
コンストラクタ付きのクラス作って、インスタンス化したり。
同じソースファイル内に定義したクラスをインスタンス化する場合であっても
コンストラクタにpublicがついてないとエラーがでるのな
この辺Javaより厳しい Java
文字列の長さを求めるには String.length()
配列の長さを求めるには 配列.length
C#
文字列の長さを求めるには String.Length
配列の長さを求めるには 配列.Length
() がついてりゃメソッドね、で終わるんだけどね
C#だとメンバの1つにプロパティというものがあって
セッターゲッター的な役割を担うようなことが書いてあった
どうやらC#の Length はこれっぽい
https://docs.microsoft.com/ja-jp/dotnet/api/system.string.length?view=netframework-4.7.2#System_String_Length
Javaの配列の length はもっとわかりにくそうなんで放置^^ string st1;
string st2;
st2 = st1 + "あああ";//未割り当てのローカル変数'st1'が使用されました。
string st1 = null;
string st2;
st2 = st1 + "あああ";//OK!
string st1;
string st2;
st2 = null + "あああ";//OK! 中身がnullだからどうのこうのの話でもないようだ
とにかく初期化されてないものを使うなみたいな?
st2みたいに代入される変数が初期化されてないならまぁいいやとか 独習C#80ページのコードを書いていて気づいたわけなんだけど
テキスト上ではstringの初期化として "" を使っている
var st = "";
string st = "";
みたいな感じで。
一応初期化は必ずやれと35ページに書いてある Console.WriteLine(Math.Floor((0.7 + 0.1) * 10));// 7 ダメ
double num2 = (0.7 + 0.1) * 10;
Console.WriteLine(num2.ToString("G16"));// 7.999999999999999
Console.WriteLine(Math.Floor((0.7 + 0.2) * 10));// 9 OK!
double num1 = (0.7 + 0.2) * 10;
Console.WriteLine(num1.ToString("G16"));// 9
浮動小数点数に計算誤差がでちゃう例なんだけど
出る場合と出ない場合を予め区別して把握するのは難しそうだ //正の小数・正の整数を2進数に変換
using System;
using System.Text;
using System.Text.RegularExpressions;
public class Program
{
public static void Main(string[] args)
{
const int Digit = 16;
string input = Console.ReadLine();
Regex rgx = new Regex(@"^([1-9]\d*|0)(.\d+)?$");
double num = 0;
if (rgx.IsMatch(input))
{
num = Double.Parse(input);
var num1 = Math.Floor(num);
var num2 = num - num1;
string num1_byte = Convert.ToString((int)num1, 2);
double a1 = 0;
StringBuilder sb = new StringBuilder();
sb.Append(num1_byte);
sb.Append(".");
for (var i = 0; i < Digit; i++)
{
a1 = num2 * 2;
a1 = Math.Floor(a1);
sb.Append(a1.ToString());
if (a1 >= 0)
{
num2 = num2 * 2 - a1;
}
else
{
num2 = num2 * 2;
}
}
Console.WriteLine(sb.ToString());
}
else
{
Console.WriteLine("適正な数値ではありません");
}
}
} いちいち紙に書いて計算してたけど
あほらしくなって自分で作ってみた
正規表現だけはコピペしてズル
小数部分の桁数は定数で指定
対象となる10進数はキーボードからの入力 独習の「同一性(Identity)」=スッキリの「等値(equality)」
→同じオブジェクトを参照、つまり完全一致(==)
独習の「同値性(Equivalence)」=スッキリの「等価(equivalent)」
→オブジェクトが同じ値をもっていれば十分(equals)
この辺業界で統一した用語使って欲しいもんだ
どちらかというと、独習の用語の方が直感的で「スッキリ」してる感じする >>337は
独習88ページ
スッキリ実践編115ページ Equals の s は三単現の s で
SequenceEqual に s がついてないのは配列という複数要素の一致を要求してるからなのかとか
ぼけぇ〜と考えてたが
でもSequence自体は単数だよなとか
まぁいいや bool? b = true;
if (b) {}
というのはダメみたいだ
if 文の条件式は bool でないとだめ
bool? はダメ @
null条件演算子(P.64) 非nullのときだけメンバにアクセス
string unspace = str?.Trim();
A
null合体演算子(P.92) nullの時のデフォルト値を与える
Console.WriteLine(num ?? 100);
B
論理積のショートカット演算(P95) 非nullのときだけメソッドにアクセス
if (str != null && str.StartsWith("http://") { }
nullを予見したいろんな手法
Bの時に@を使えないか考えてみたんだけど
条件式に
str?.StartsWith() を置いてしまうと、ここが bool? つまりnull許容型の bool と判定されてしまう
if文の条件式に bool? は使えないようなのでエラーが出てしまう
null がきちゃうと判断出来なくなっちゃうから仕方ないのか
Bは大事なんじゃないかと思ってる
VBAのプログラム組んでたときに論理積・論理和系の条件式でよくわからないエラーがでてたのは
これじゃないかと、今思えばだけど。
当時は null を放置しておくと条件式でエラー出ちゃいがちだということを経験からわかってたんで
とりあえず適当な値入れて回避してたんだけど。 >>324
>>325
これ>>335でぽちぽちやりながら
このへんをさらに掘り下げてたんだけど
根本的な部分が理解できた
一つ一つの処理であったりルールは理解できるんだけど
なんだかもやもやがついて回る
複数の疑問点が浮かび上がるんだけど、最終的に集約されていくのはこの疑問
Q.「なぜ負の数を表すのにわざわざ補数を使うのか」
言ってることはわかるけど、そもそもなんでこんなことしてんの的な疑問というのは
どこが謎の源泉になっているのか、自分の思考を分析するのがやっかいだ
自分なりにたどり着いた答えがこれ
A.「コンピュータは引き算をしたくないから」
もっというと
A.「スイッチON/OFFの世界だけで計算したいから」
細かく経緯を説明し始めると長くなるんだけど、
ビット演算やらこの辺の分野全てにわたる話なんだと思う 補数が関係してくるのはbit演算のうち否定演算
では、
& 論理積
| 論理和
これらはどう使われるのか
まだ学んでいないので想像でしかないんだけど、例えばこんな感じなのか
絵がいくつかあって
A,B,Cと名前を付けるとする
これらの絵は4色の絵の具(赤・青・緑・黒)を使って描かれているのだが
全ての色を使っているわけではない
どの色を使っているかによって場合分けして処理していきたい
このような処理をするときに、ある工夫をしてみる
まず色ごとに特定の2進数を割り当てる(()内は10進数)
int r = 0b0001; //赤(1)
int g = 0b0010; //緑(2)
int b = 0b0100; //青(4)
int w = 0b1000; //白(8)
見て分かるように1をずらしただけのものだ
スイッチのように考える
一番右のスイッチがONのものは赤、みたいな感じ
そして絵A・B・Cについても2進数で表現する
このとき、それぞれの絵がどの色を使っているかによってスイッチON/OFFを切り替える
int artA = 0b0011; //A(3) 緑と赤を使用
int artB = 0b1001; //B(9) 白と赤を使用
int artC = 0b1111; //C(15) 全色使用
前提はここまでで、具体的にこれらをどうつかっていくかを説明(想像)する
例えば、絵Aが赤の絵の具を使っているかどうかによって条件分岐させたいならこうする
if ((artA & r) == 0b0001) { }
絵Aと絵Bで使っている色を全部列挙したいなら
int result = artA | artB;
としてから result について分析すれば分かるだろう
分析方法を想像すると・・・
if ((result & w) == w) { } //白を使っている
みたいな感じか
似たようなことがTCP/IPの本を読んだときも書いてあったような気がする 結局、スイッチON/OFFの世界をどうやってうまく利用していくかなんだろうな
すごいもんだ ちなみにだけど
if ((artA & r) == 0b0001) { } ○
if (artA & r == 0b0001) { } ×(エラー)
なので注意 これは演算子の優先順位が
==
のほうが
&
|
より高いから public class Program
{
public static void Main(string[] args)
{
for (var i = 0; ; i++)
{
int num = new Random(i).Next(1000);
Console.WriteLine(num);
if (num == 500)
{
break;
}
}
}
}
乱数生成して遊んでた
乱数好き
Randomクラスのコンストラクタに渡す引数は「シード値」といって
これが同じだと生成される乱数は変らない
ここはJavaとは違うよね確か。
まぁそういうわけでシード値を次々に変えていくために
カウンター変数をシード値として流用
練習コードを書くときに乱数を使うとちょっと動きのあるコードを書けるので面白い シード値はJavaでも同じかな
シード値のデフォルトが違うかもしれない
めんどうなんで調べないけど スッキリわかるSQL入門 第2版 ドリル222問付き! (スッキリシリーズ)
中山清喬
ぽちった 5章やってたんだけどラムダ式が結構出てくる
先に押さえておきたくて10章に一時的に出張中
この辺はJavaでは薄くしかやってないので
しっかりやらないと理解できない
delegateから始める VBAのちょっとしたのを組まなければならなくなってちょいと中断
さっさと終わらせたい あああああ、vbaだいぶできてきたaa
もう少し! いつもやらかすのは
Range(Cells(i, j), Cells(k, l))
みたいにRangeの指定をCellsを使ってやるケース
このとき、
With ThisWorkbook.Worksheets("なんとか")
で囲ってるなら
Cellsの前にもドットが必要なんだけど、これを忘れる
そしてそれに気づかない
今日もやらかしてしばし作業がとまった C#
Splitクラス
区切り文字はchar型なのでシングルクオーテーションを使わないといけない
string型のダブルクオーテーションを使うとエラー
すぐ忘れる
あと、区切り文字で new[] を使わない時は { } をつかってはダメ 正規表現
\d{2,4} ○
\d{2, 4} ×
なんとスペース入れるとダメ
今気づいた
こういうのって実際にコードを打ち込んでみて失敗しないと気づけない
あと、おれはそんな記憶力良くないんで、実際にコード打たないと覚えた気になってて
実際は覚えてないということになる
めんどうだけど、とにかくできる限り全てのコードを打って試していこう
テキストのコードを自分なりに少し変えて試す
いや、これがまためんどくさいのだが、ためになる 命令文の中に「:」が入ってくるようなクラスを説明する時に
Console.WriteLineの中に「:」を入れるのはやめて欲しいわ
そうでなくてもできる限り記号を入れないで欲しい
例えば
Console.WriteLine($"位置:{m.Index} 長さ:{m.Length}");
とかいうコード
Console.WriteLine($"位置は{m.Index} 長さは{m.Length}");
でいいだろ
このコードでは「:」は混同の原因になりにくいかもしれないけど
例えば三項演算子とか入ってきたりした場合とか ふぅ 5章の重さは半端ないわぁ
2周目なんだけどしっかりやってるとなかなか進まない
1周目で理解しそびれた部分押さえるのが精一杯 せめて9章までやらないと基本分野網羅できない感じする
ここまでやると
コレクション
オブジェクト指向の各分野
を大体押さえることができそう
新版のスッキリSQLも届いたのでちょくちょく目を通していこうかな
GUIアプリをC#でつくるための参考書も買ってあるので
のぞいてみたんだけど、JavaのSwingで作るよりずいぶん簡単そうな感じがする
クラス定義の後ろの方に「:」があるようなコードが書いてあったのだが
これはまだやってない分野だ
自動で作られたコードを隅々まで理解できるようになりたいので
独習の方を先に進めることにする 5章2周目クリア
もう一周ぐらいやらないとダメなことは分かっているが
復習はまたあとにして6章に進む Dictionary で使っている Red-Black tree だけど、なるほどなぁ
これは foreach で取り出すとき、できるだけ左側の下の方から探索していくのか
こういうアルゴリズムって面白いな >>358の「:」はインターフェースの実装だったようだ
この辺はJavaと記述が違うからC系から引き継いだものかな コレクションも配列もSortの規則を変えるにはラムダ式を使おう!
var l = new List<int>() { 100, 200 };
l.Sort((x, y) => (-x) - (-y));
Console.WriteLine(string.Join(",", l));
var ary = new[] { 100, 200 };
Array.Sort(ary, (x, y) => (-x) - (-y));
Console.WriteLine(string.Join(",", ary)); >>364は
(-x) - (-y) → y - x
だな すっきりとちがって独習はたまに誤解を招きそうな表現があるんで気をつけないといけない
おまえらぐらいだとこの程度の説明でわかるだろ?あ?
みたいなのを感じる
が、おれは残念ながら分からない
この説明端折りすぎてんなと思ったらコードいろいろ書いて確認してる C#ではインスタンス経由でstaticメンバにアクセスできないのか
Javaだとスッキリ入門で複数の勇者インスタンスが、共有のお金にアクセスするために
static int maney を定義していたが、ああいうのはだめなのか
独習278ページ
「クラスメソッドはオブジェクト経由では呼び出さない」 Mathクラスがなぜ静的クラスなのかを説明している文だけど
関連した機能を一つのクラスにまとめることでわかりやすいから、
みたいな説明がされている
これって説明になってないと思う
そうじゃなくて、このような数学的な処理ってのは
引数を渡して即座に処理をして結果を返せば足りるんで
いちいちデータをフィールドに格納しておく必要はないから
ってのが最大の理由じゃないの
そもそもインスタンス化の最大の利点はインスタンスごとに
異なるフィールド値を格納しておくことができる点でしょ
そこを説明してないのってちょっとあれだと思うんだけどどうなんだろうか
ちがうんかね
独習287ページ 値型の値渡し
参照型の値渡し
値型の参照渡し
参照型の参照渡し
今読んでてちょい混乱したけどもうおk
「値渡しはコピー」で
「参照渡しはこれをみてね」
みたいな理解でいけた
これCでもやったような記憶があるがデジャブだろうか
面倒だがひとつひとつテストコードを自分で書いて確かめてみようか 自作のJavaプログラムだけど、ちょっと前から動かなくなってた
どうやらJDKのパージョンアップにともなって仮想マシンにアクセスできなくなっていた
ユーザー側の問題になるんだけど、バージョンアップ程度で環境変数やらいじらせるのっていまいちだな
やっぱwindowsアプリつくるならC系なのかね 素数かどうか調べるプログラムに感心した
平方根となる小数を切り捨てて整数にする
この整数の値になるまで2から順に除算をして
余りが0になるものがあるか調べる
ヒットする場合は素数ではない
var prime = true;
for (var i = 2; i <= Math.Floor(Math.Sqrt(num)); i++)
{
if (num % i == 0)
{
prime = false;
break;
}
}
中学校の数学で必死こいてやってた作業と全く同じだ
「真ん中」まで調べる的な IEnumerable<>を戻り値として使うイテレーター構文は
foreachを簡単に使えるようにするために利用される
(foreachを使えるようにIEnumerableインターフェースを実装するのは大変なので)
ポイントは
yield return (と、yield break) 独習318ページの3.@の解答
readonlyと書いてあるけどprivateだよなこれ
いちいちここに書いてないけど誤植がちらほらある
誤植だと思い違いしてるケースも考えられるので実際困る プロパティはセッターゲッターつまりアクセサーメソッドの発展系 インデクサーの説明わかりにくいなあ
インスタンス名[index] でインスタンスを利用したいときに使うみたいな感じか
フィールドが配列の時に使うとうまい具合に組み合わさって利用価値があるみたいな インスタンス名[ ] の形式で利用したいなら自由に使えるようなものなので
例えば[ ]の中が要素番号的なものでなくても使えると。
class Test
{
public string this[string st]
{
get { return st; }
}
}
class Program
{
public static void Main(string[] args)
{
var t = new Test();
Console.WriteLine(t["aaa"]);//結果「aaa」
}
} で、フィールドが配列の時に、インデクサーを組み合わせて使うと
真価を発揮できると(まるで真価を発揮できてない例だが・・・・
class Test
{
string[] ary = new string[3];
public string this[int index]
{
set { ary[index] = value; }
get { return ary[index]; }
}
}
class Program
{
public static void Main(string[] args)
{
var t = new Test();
t[0] = "aaa";
Console.WriteLine(t[0]);//結果「aaa」
}
} this[string st]
this[int index]
の部分が大事で、こういう使い方をすると便利だなと思うような場面で使えば良いのかな
特にインスタンスを配列ライクに使いたい場合
インスタンスをforで回しながら何かをインスタンス内のどこかに代入していきたいとか 忙しくなってきて時間裂けなくなってきたけど
少しずつでもいいんでやっていこうと思う マッマに新しいPC買ってもらっただで
今までは簡単なアプリをGooglePlayにリリースしてきたけど、ようやく本格的なアプリが作れそうや
あとは今運営してる比較サイトもAjaxで非同期にデータベースとアクセスできるようにしたい
Googleが提供してるFireBase使えば、サーバーサイドの開発する必要すらないみたいやな
便利な時代になったで ぐぐるさんは優秀なapi無料で公開してくれてるからありがたいんだけど
セキュリティ固めてる環境からアクセスするようコードに仕込んでると
そのうち担当者から怒られそうなのがネック ふぅ 8章まで終わった
覚えてるのかと言われたらスッカスカだが一応理解はした
スッカスカで先に進むと理解できなくなりそうだが
ここは敢えて進むのだ >>343は列挙型で使うようだ
「<<」演算子を使うと、色ごとに2進数を割り当てる時に、
1をズラしたものを簡単に定義できる
int r = 1; //0b0001 赤(1)
int g = 1 << 1; //0b0010 緑(2)
int b = 1 << 2; //0b0100 青(4)
int w = 1 << 3; //0b1000 白(8)
こんな風に ちょいGUIに寄り道する
入門本買ってあるので簡単なものを作れるようにしていく
Javaの時と同じ流れでやっていく slnファイルをクリックするとそのソリューションがVisual Studioで立ち上がる
ふむふむ 違う本読むとまたそれはそれで勉強になるわぁ
Visual Studioの使い方適当にやってたけどいろいろと説明してくれててありがたい
オブジェクト指向がどういう流れで生まれてきたのかとか、
この辺は著者によっていろんな説明してくるけど、この本の説明は頭にすっと入る
データベースの誕生に関連させてオブジェクト指向の誕生を説くあたり
+
別のプログラムを組む時に、他のプログラムから一部借用してきたりとか、そういう再利用のしやすさ ふーむ
これはVBEのフォーム組むのとすごーく似てるね
VBAのプロパティってC#のプロパティと同じものなのか
オブジェクト指向のインスタンスやプロパティ、メソッドをコードからじゃなくて
VisualStudioのGUIを使って説明するとか斬新だわ
あと、デザイン画面でぽんぽんとコントロールを貼り付けていくと
勝手にコードが背後で生成されていくわけなんだけど
このコードも理解したいな
JavaでSwingで試してた時は全部自分で書いてたわけで
C#では相当楽できるんだが、仕組みは分かっておきたい
パーシャルクラスとかさっき勉強したばかりのを使ってて復習にもよさそう Program.csがMainメソッドを持つ主か
ここからスレッドを走らせ
編集対象となっているForm1.csのForm1クラスのインスタンス生成
Form1クラスのコンストラクタがInitializeComponentメソッドを呼ぶ
これはデザイン用に用意されているForm1.Designer.csにあるメソッドで
ちょうど、Swingでコンストラクタの最初の方に書いていたようなコントロールの配置とかが書いてある
各コントロールの実際の動きはForm1にメソッドとして記述していくわけか
Javaでのイベント系の処理とどの程度類似してるか
そっくりというわけではなさそうだ この辺を理解するには独習の最後の最後を読んだほうが良さそうだ
いろいろ調べてみたんだけど、マルチキャストデリゲートなのかな
それだけでは説明できない要素が加わっているようなので、これを理解するには
独習の最後、ほんとに最後なんだよなこれ、ここを読むほうがよさそう
で、明日読もう
今日は終わり public delegate void MyEventHandler(object sender, EventArgs e);
class Program
{
public event MyEventHandler myEvent;
public void handler(object o, EventArgs e)
{
Console.WriteLine("handler called");
}
public static void Main(string[] args)
{
Program target = new Program();
target.myEvent += target.handler;//+= new MyEventHandler(target.handler)も可
target.myEvent(target, EventArgs.Empty);
}
}
eventを使った簡単な例
new使ってデリゲートに代入する方法を忘れてた
ネットで調べるとこの書き方を使ってるものが多かったので何だこりゃと思ったんだけど
よくよく独習読み直したら最初の方に書いてあった
target.myEvent(target, EventArgs.Empty); に相当するコードは
windowsアプリでボタンとかをデザイン画面で作った時に自動生成されるコードには見当たらない
ボタンを押したときに内部的にこのコードと同じ機能が働くことで
デリゲートに代入されているメソッドが実行されるという流れなのか
object o
EventArgs e
この2つの引数もわかりにくい
oはイベントの呼び出し元のインスタンスを意味するみたいだ
EventArgsはまだよく分からない o は 「イベントを発生させたオブジェクト」
と書いた方がよかった
EventArgs e は 「ハンドラに渡す引数」
上の例だと渡すべき引数がないのでEventArgsクラスのEmptyプロパティがついている
ここ↓を参考にしてるんだけど、おれにとっては一番わかりやすい
http://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_013/csharp_abc01.html
EventArgs eについてもここ↓
http://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_013/csharp_abc02.html
で理解出来そうだ なんとなくだけど、分かった気がする
ハンドラ、つまり、実際に実行されるメソッドなんだけど、これに情報を渡したい時がある
このとき、場合によってどういう情報を渡したいのかは様々なので、
自分でクラスを作ってそこに情報を記録してわたせということか。
その際、EventArgsクラスを継承してクラスを作る事になる
その例となるコードが上に上げたURL2個目に書いてある
では、これがwindowsアプリを作った時に自動生成されるコードにどういう形で反映されているかだ
e に何を情報として詰め込んで渡しているかは、自動生成されたコードには書かれていない
これはボタンとかのAPIが内部的にやってるようだ
ここでよくよく考えてみると、JavaのSwingでも同じような e が出てきた
例えば>>109
右クリしたときのmouseClickedメソッドはMouseEvent eを引数として受けとるわけだけど
この e を使って、showメソッドに e.getComponent(), e.getX(), e.getY() という引数をわたしている
この getComponent(), getX(), getY() こそが内部的に e に格納されいる情報というわけか
まぁここでは情報というかメソッドではあるんだけど、e について用意されたものなわけだ
同じ事をC#でやろうとしても、ちょっと勝手が違う this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(51, 12);
this.label1.TabIndex = 0;
this.label1.Text = "こんにちは";
this.label1.DoubleClick += new System.EventHandler(this.label1_DoubleClick);
private void label1_DoubleClick(object sender, EventArgs e)
{
label1.Text = "ダブルクリックされました";
MessageBox.Show(sender.ToString() + "\n" + e.ToString());//@
}
これは、「こんにちは」と書かれたラベルをデザイン画面で作った時に自動生成されたコード
ラベルをダブルクリックしたときに「ダブルクリックされました」とラベルの表示が変るようにしてある
ここに、今問題としている引数2つ、senderとeの内容をToString()で表示させる一行を加えた@
@の結果はこう↓
System.Windows.Forms.Label, Text: ダブルクリックされました
System.Windows.Forms.MouseEventArgs Javaでは e と打ったあと、自動補完システムが e について使えるフィールドやメソッドを
示してくれた
C#でもインテリセンスが示してくれるんだけど、
Objectクラスのメンバのようなものしか表示されていないようだ
そこで、この一行を@の代りに使ってみる
MessageBox.Show(sender.ToString() + "\n" + ((MouseEventArgs)e).Clicks);
これは、((MouseEventArgs)e). まで書いたときに、インテリセンスが羅列した候補からClicksを選んだ場合だ
このようにMouseEventArgsでキャストすることで、Javaと同じように e の実際のメンバが表示できた
要は、基底クラスのEventArgsのままだとメンバが表示されないので
派生クラスのMouseEventArgsにキャストし直したというわけだ
e がどのクラスなのかは、>>394でやったようにToString()を使って知ることができる
ちなみに、結果は
System.Windows.Forms.Label, Text: ダブルクリックされました
2
とでる
e から、クリックの回数である 2 という情報を引き出せたことになる
一応満足した ((MouseEventArgs)e).X
((MouseEventArgs)e).Y
相対座標もこれで取得できる
ラベルの左上が原点かな?たぶん
C#はこの辺の理解全くなしでGUIアプリを組めるという点では楽だな
その反面理解なしに進んでしまうから、そのうち壁にぶつかりそうだけど
これまでコンソールアプリだとVisualStudioの画面に、
windowsアプリだとMessageBox.Show()でメッセージボックスに
文字情報を出力してきたけど
これからはフォーム上に作ったテキストボックスに出力する方がよさそうだ
こぴぺもできるしね
ここにコード貼るときにタブというかスペースを加工しないと
うまくインデントを表現できなかったので、正規表現使えるエディタで置換してた
ただ、ちょっと使いづらい
そこで、この加工をする専用のwindowsアプリを自分で作ってみようかなとか思ってる
簡単そうだし役に立ちそう でけた〜
初のC#アプリ
やっぱりC#は簡単に作れるなぁ
正規表現でちょっと迷ったところがあったけど
サブマッチパターンに名前を付けて処理することで解決した private void button1_Click(object sender, EventArgs e)
{
string textBox1Value = textBox1.Text;
var rgx = new Regex(@"(?<wideSpaces>^( )*)(( ){4})", RegexOptions.Multiline);
while (rgx.IsMatch(textBox1Value))
{
textBox1Value = rgx.Replace(textBox1Value, "${wideSpaces} ");
}
textBox2.Text = textBox1Value;
}
目的は、
マルチラインのtextBox1に表示されているコードの全てのインデント(半角スペース4個)を
全角スペース1個に置換し、結果をtextBox2に表示させること
方法はいろいろあると思うけど、こういう流れでやった
マルチラインモードの正規表現で文字列先頭にある半角スペース4個セットを検索して、
これを全角スペース1個に置換。
半角スペース4個セットは複数並んでいる場合もあるため、一度では処理できない
そこで、whileで半角スペース4個セットがなくなるまで回す方向で考える。
正規表現に少し手を加える
文字列先頭にある「0個以上の全角スペースと、これに続く半角スペース4個セット」を検索し
これを置換する。0個以上の全角スペースとは、置換済のインデントを意味する
どのように置換するかだが、
0個以上の全角スペースはそのままで、半角スペース4個だけを全角スペース1個へ置換する
0個以上の全角スペースはそのまま残すため、
この部分を丸括弧でくくってサブマッチパターンとし、wideSpacesと名前を付ける
(?<wideSpaces>^( )*)
そして、置換後文字列にwideSpacesを利用する
最初はsplitして一行ごとに処理していたけど、せっかくだからマルチラインモードとかの
復習も兼ねて、てんこ盛りでやってみた
文字列の途中に半角スペース4個がないと仮定するならもっと簡単なのだが
そこはこだわってちゃんと作ってみた
貼ってあるコードは、もちろん作ったばかりのアプリで置換作業をしたもの。 windowsアプリの入門本使っていくつか作ってるんだけど簡単に作れるわ
今の知識だけでもいろんなもの作れそう
Javaでも外部ライブラリやら導入すればもっと簡単にできたのかもしれないけど
VisualStudioだと追加導入の手間いらずでこれだけ作れるのはすごい
今ちょうど>>119-121に相当するコードを書いていたのだけど、ずっと簡単にかける
前回閉じた場所で開く機能も試しにやってみたいがどうすっか 閉じたときの場所をlocation.logに記録して
開いた時にlocation.logから座標を読み取って再現するという
Javaでもやった例のパターンなんだけど
開いた時にロードする時、当然コンストラクタに記述するんだろと思いきや
windowsアプリだとイベントとして記述するみたいだ
これも斬新だな private void Form1_Load(object sender, EventArgs e)
{
string str = null;
int x = 0;
int y = 0;
if (File.Exists(Path))
{
using (sr = new StreamReader(Path))
{
str = sr.ReadToEnd();
}
var rgxX = new Regex(@"(^x=)([0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)([0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
if (int.TryParse(strX, out x) && int.TryParse(strY, out y))
{
this.Left = x;
this.Top = y;
}
else { SetCenter(); }
}
else { SetCenter(); }
void SetCenter()
{
this.Left = (Screen.PrimaryScreen.Bounds.Width - this.Width) / 2;
this.Top = (Screen.PrimaryScreen.Bounds.Height - this.Height) / 2;
}
}
座標をロードするハンドラ
Pathはクラス定数としてlocation.txtが定義されてる
location.txtにはフォームを閉じる際、
x=100
y=150
のような形式で座標が登録されるようになっているので、
ロードハンドラではこれを読み込むことになる
正規表現はお気に入りのサブマッチパターンを使った
TryParseでx,y座標ともにintへの変換が成功した場合のみフォーム表示の座標として利用
失敗したときは内部メソッドのSetCenterを呼び出してモニタ中央に表示させる フォームを開く時にモニタ中央に表示させるコードとしてこういうものがある
this.StartPosition = FormStartPosition.CenterScreen;
これを使うのであれば、コンストラクタに書く必要がある
上のロード用のイベントハンドラはどうやらフォームが表示されたあとに動いているようだ
ハンドラにStartPositionを書いてしまっても今更ということになってしまう
そのため、SetCenterメソッドでは、
すでに表示されているフォームの位置を変更するという形をとっている 例えばカレンダーをつくるとして、日付を表示するパネルが42枚並んでるとする
このとき各日付パネルをクリックすることで何らかのアクションを起こしたい時
Form1クラスに42個のイベントハンドラを並べるのはどうなんだと思い調べてみた
https://dobon.net/vb/dotnet/control/buttonarray.html
配列を利用することになるんだけど
いくつかの方法があるようだ uint x;
Console.WriteLine(uint.TryParse("-10", out x));
結果はfalse
これを利用することで、モニタの表示画面からはみ出した座標が記録されていた場合
次に開く時はモニタ中央に開けるかやってみた
string str = null;
uint x = 0;
uint y = 0;
if (File.Exists(Path))
{
using (sr = new StreamReader(Path))
{
str = sr.ReadToEnd();
}
var rgxX = new Regex(@"(^x=)([0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)([0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
if (uint.TryParse(strX, out x) && uint.TryParse(strY, out y))
{
this.Left = (int)x;
this.Top = (int)y;
}
else { SetCenter(); }
}
else { SetCenter(); }
void SetCenter()
{
// this.StartPosition = FormStartPosition.CenterScreen;はForm_Loadイベントハンドラでは機能しない
this.Left = (Screen.PrimaryScreen.Bounds.Width - this.Width) / 2;
this.Top = (Screen.PrimaryScreen.Bounds.Height - this.Height) / 2;
}
一応いけるんだけどサブモニタ上ではうまく機能しないようだ
マイナスのy座標が記録されている場合でも、そのままの座標で開いてしまう
メインモニタ上では機能するので、とりあえずいいか あーちょいまてよ
正規表現にマイナスが含まれてないな
この辺でおかしくなってそうだ
今いそがしいからあとから触ってみよう >>404の正規表現部分だけを修正
var rgxX = new Regex(@"(^x=)(-?[0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)(-?[0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
これでサブモニタでマイナス側に飛び出しても大丈夫
可能性として、下とか左に飛び出す場合もあるわけで
そういう場合には対応できていない
そう難しいわけではなさそうだけど >>404をさらにアプデ
private void Form1_Load(object sender, EventArgs e)
{
string str = null;
int x = 0;
int y = 0;
if (File.Exists(Path))
{
using (sr = new StreamReader(Path))
{
str = sr.ReadToEnd();
}
var rgxX = new Regex(@"(^x=)(-?[0-9]+)", RegexOptions.Multiline);
var strX = rgxX.Match(str).Groups[2].Value;
var rgxY = new Regex(@"(^y=)(-?[0-9]+)", RegexOptions.Multiline);
var strY = rgxY.Match(str).Groups[2].Value;
if (int.TryParse(strX, out x) && int.TryParse(strY, out y))
{
this.Left = x;
this.Top = y;
}
else { SetCenter(); }
}
else { SetCenter(); }
var scr = Screen.GetBounds(this);
const double RemainLimit = 0.6;
bool result = true;
result &= (scr.X + scr.Width - this.Left > this.Width * RemainLimit);
result &= (this.Left + this.Width - scr.X > this.Width * RemainLimit);
result &= (scr.Y + scr.Height - this.Top > this.Height * RemainLimit);
result &= (this.Top + this.Height - scr.Y > this.Height * RemainLimit);
if (!result) { SetCenter(); }
void SetCenter()
{
this.Left = (Screen.PrimaryScreen.Bounds.Width - this.Width) / 2;
this.Top = (Screen.PrimaryScreen.Bounds.Height - this.Height) / 2;
}
} >>408
uintは座標がマイナスの場合しか使えないロジックなのでやめやめ
こんな風にした
保存してある座標をそのまま使って一旦フォームを表示させる
もちろんlocation.txtがないとか、数値が想定通りに記録されてない場合はメインモニタ中央へ表示
その上で、フォームがモニタ画面から飛び出し「すぎてる」場合はメインモニタ中央へ移動
RemainLimit = 0.6はフォームの幅・高さの6割がモニタ画面に映っているならOK
それを下回ったらメインモニタ中央へ表示、という定数
これで上下左右・メインモニタ/サブモニタ関係なしにオールマイティにはみ出し補正可
resultのロジックだけど
result = result && (〜〜〜);
でやってたんだけど、もしかしてとおもって
result &= (〜〜〜);
にしたら行けたんでこれにした
要は if を並べて階層深くしたり、条件式が長くなるのが嫌だっただけなんだけど
これはクラスにまとめてこれから作っていくアプリにも使っていこうかなぁ〜 フォームを一旦表示させてから問題あるなら移動させるという2段階の方法を採った理由は
一度に所定の位置へ置こうとすると、scr.X とかがうまく取得できなかったから どうでもいいような部品なんだけど
うまくいったんで満足である
独習はまだしっかり頭にはいってないところがたくさんあるけど
辞書的に使うのにも適してる
今のGUIの入門本しばらくやってから、独習の残り1割ぐらいだけど
まだ読んでない部分読むか
復習も何度かしないといけないな >>403で書いた複数コントロールを束ねる処理についてなんだけど、
今やってる入門本でも紹介されていた
複数のボタンをひとつのハンドラでコントロールする方法を使う
デザイン画面で複数ボタンを選択してから
右下プロパティウィンドウ→イベントタブ→Clickの項目にハンドラ名を入力してEnter
で、Form1に選択した複数ボタンに一括対応するハンドラが自動生成
ここでひとつ問題がある
Form1のこのハンドラを見ただけでは
このハンドラがどのコントロールに対応してるかがわからない
覚えてりゃいいんだけど、またはコメント書いておけばいいとかいう話ではあるんだけど
やはりちゃんとコード上で確認できるべき
そこで、確認方法
左上ソリューションエクスプローラウィンドウ→Form1をクリックして展開
→Form1.Disigner.csをダブルクリックして開く
コード上に
「Windows フォーム デザイナーで生成されたコード」
というregionがあるので左端+をクリックして展開
この中にbutton2に対応する部分がある
//
// button2
//
this.button2.Location = new System.Drawing.Point(13, 13);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(125, 100);
this.button2.TabIndex = 0;
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.buttons_Click);//←ここ
複数ボタン対応ハンドラの名前は「buttons_Click」としたのだが、
button2.Clickにbuttons_Clickハンドラが登録されている
ここからbutton2.Clickがbuttons_Clickメソッドによりコントロールされることがわかる
同じようにbutton3, button4, button5 ・・・・といったbuttons_Clickハンドラにより
コントロールされるボタンのコードにも、buttns_Clickが登録されているのがわかる 複数コントロールを一括してひとつのハンドラにまとめるには
対象となるコントロールをぽちぽちとctrl+左クリすることで選択していくわけだけど
こういう方法によると、対象外のオブジェクトを間違えて選択してしまうことがある
実際に自分がやった間違いとしてこれ↓
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.button10);
this.splitContainer1.Panel2.Controls.Add(this.button7);
this.splitContainer1.Panel2.Controls.Add(this.button4);
this.splitContainer1.Panel2.Controls.Add(this.button9);
this.splitContainer1.Panel2.Controls.Add(this.button6);
this.splitContainer1.Panel2.Controls.Add(this.button3);
this.splitContainer1.Panel2.Controls.Add(this.button8);
this.splitContainer1.Panel2.Controls.Add(this.button5);
this.splitContainer1.Panel2.Controls.Add(this.button2);
this.splitContainer1.Panel2.Click += new System.EventHandler(this.buttons_Click);//←ここ
this.splitContainer1.Size = new System.Drawing.Size(412, 396);
this.splitContainer1.SplitterDistance = 54;
this.splitContainer1.TabIndex = 0;
ボタンの背景にあるパネルを間違えてクリックしてしまい
パネルがbuttons_Clickハンドラの支配下に置かれてしまっている
このままだとボタンではなく背景のパネルをクリックしただけで
buttons_Clickハンドラに記述されている処理が実行されてしまう
パネルが選択されたかどうかは、デザイン画面上は非常にわかりにくく間違いを招きやすい
選択されたオブジェクトが羅列されているウィンドウは見当たらない
注意深くやっていくしかないというところか ところで、>>413のようにミスしてしまった場合の対応なのだが、
実際、どのオブジェクトがどのハンドラの支配下にあるのかは>>412でわかるように
「隠された」コードを見るしかない
しかも、この部分にはこんなコメントが記述されている
/// <summary>
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
/// </summary>
>>413の「//←ここ」の行を削除すればいいんだろうなということは
今まで貯めた知識で分かってはいるんだけど、本当にいいんだろうかと。
別の方法により、「自動的に」削除させる方法があるんだろうか
まぁよくわからないので、マイクロソフトさんに逆らって勝手にコードを削ることにする
この辺の知識なしでVisualStudioをさわる人はどう処理をしているのか
もう一度対象オブジェクトを全て削除してから自動生成し直しているのかな
なんだかんだこだわって理解しようとしてると、そのうち役に立つもんだ >>412のこれだけど
this.button2.Click += new System.EventHandler(this.buttons_Click);
this.button2.Click += new EventHandler(this.buttons_Click);
だとエラーがでる
コードが記述されているのはForm1のパーシャルクラスなんだけど
Form1.csにはusing System;が記述されているから省略してもいいんじゃないかと思ったのだが
パーシャルクラスであってもファイルが違うなら、冒頭にusing System;入れないと
省略できないようだ
実際にusing記述したら省略してもいけた
ここは基本的にユーザーがいじらない前提でコードが自動生成される場所なので
usingでの省略は逆に足手まといになるのかな イベントドリブンモデルについて再考
>>391なんだけど、こう書き直してもいい
public delegate void MyEventHandler(object sender, EventArgs e);
class Program
{
public event MyEventHandler myEvent = (sender, e) => { }; //←ここ注目
public void handler(object o, EventArgs e)
{
Console.WriteLine("handler called");
}
public static void Main(string[] args)
{
Program target = new Program();
target.myEvent += target.handler;//+= new MyEventHandler(target.handler)も可
target.myEvent(target, EventArgs.Empty);
}
}
独習567ページのコードに含まれているこれ、
public event KeyCommandEventHandler KeyCommand = v => { };
の理解のために比較対象としてあげてみた
独習の方の
v => { }
は空のメソッドを意味する。
= v => { }
により空のメソッドを初期値としてKeyCommandに代入していることになる
そもそもデリゲート型変数であるKeyCommandにはメソッドが入る
int型変数の初期値として 0 を入れることがあるように
デリゲート型変数にラムダ式を使って空メソッドを初期値として代入しているわけだ。
もちろん初期値を代入することは必須ではないので
public event KeyCommandEventHandler KeyCommand;
と書いてもエラーはでない(はずだ!)
同様に、初期値を設定しない>>391のようなコードも問題なく動く(これは確認済)
上で書きなおしたコードは
(sender, e) => { }
を初期値としてデリゲート型変数myEventに代入している
ラムダ式で表したメソッドだ
デリゲートは引数2つと戻り値voidなので、これに対応した形になっている >>417修正
イベントドリブンモデルにおいては、もちろん通常のデリゲートでも同じような事が
言えるのかもしれんが、
いや、もっというと初期値というもの全般について言えることなんだけど
変数に中身が入ってない状態で呼び出されてしまった場合に備えて
やはり空のメソッドでも入れておくべきだ
初期値についてはいろいろルールがあったはずだが忘れたw
調べるのが面倒なのでおいおいということにしよう
クラス変数とローカル変数でも変ってきたはず
とりあえずこれだけは確認した。下のコードはエラーがでる
初期値代入なしで、中身となるメソッドも入れずに実行した場合
public delegate void MyEventHandler(object sender, EventArgs e);
class Program
{
public event MyEventHandler myEvent;
public void handler(object o, EventArgs e)
{
Console.WriteLine("handler called");
}
public static void Main(string[] args)
{
Program target = new Program();
target.myEvent(target, EventArgs.Empty);
}
} VisualStudioでは、ボタンクリックなどで使われるデリゲート型変数の初期値は
どうなっているのか調べてみた
>>412に従って表示されたbutton2.ClickのClickにカーソルを合わせて右クリ
→定義へ移動
public event EventHandler Click;
初期値となる空メソッドは代入されていないように見える
public event EventHandler Click = (sender, e) => { };
こう書き加えてやろうと思ったがもちろん勝手には書き込めないようだ
中身となるメソッド(ハンドラ)が自動生成されて、自動で代入されていくので
初期値がないことによって生じる問題は基本的には起きないと想定した結果か 忘れてた
>>418で、
初期値設定した上で、中身のメソッドを入れなかった場合はエラーなし
何も表示されずに終了
public delegate void MyEventHandler(object sender, EventArgs e);
class Program
{
public event MyEventHandler myEvent = (sender, e) => { }; //初期値代入
public void handler(object o, EventArgs e)
{
Console.WriteLine("handler called");
}
public static void Main(string[] args)
{
Program target = new Program();
//中身メソッド代入しない
target.myEvent(target, EventArgs.Empty);
}
} private void buttons_Click(object sender, EventArgs e)
{
if (((Button)sender).Text == "あたり")
{
MessageBox.Show("あたり!");
}
}
イベントハンドラの第一引数の具体的な使い方
>>395-396では第二引数 e をやったけど
今回は第一引数のsenderについて
使い方は e と同じで、object型になっているので派生クラスにキャストすることで
senderに格納されてるデータを引き出せる
派生クラスの正確な名称は
MessageBox.Show(sender.GetType().ToString());
を一旦置いてみることで取得できる
この場合
System.Windows.Forms.Button
と表示されるので「Button」が派生クラスだと分かる
Javaだとこの辺のデータも e に格納されていたと思う
>>109の e.getComponent() がそれか 派生クラスの名前は
Form1.Designer.csを見ても分かる
this.button2 = new System.Windows.Forms.Button();
ボタンには Buttonクラスのインスタンスが代入されている フォームの大きさを変えたときにボタンとかテキストボックスが自分の思う通りに
追随してサイズを変えたり、または固定されたり・・・といったことをやってた
SplitContainer→FixedPanel, IsSplitterFixed
TableLayoutPanel
などなど・・・
なれないと思った通りにするのに時間かかる イベントハンドラの中で、Form1の座標を取得したいとき、
イベントハンドラに渡されるsenderの実体がForm1の時は問題ない
((Form1)sender).Left
((Form1)sender).Top
senderの実体がButtonの時は同じようにやってもエラーが出る
ButtonなのにForm1型にキャストしようとするからだ
この場合はこうする
((Button)sender).FindForm().Left
((Button)sender).FindForm().Top
Buttonが位置するFormを取得するメソッドFindFormを使えばいい ■ このスレッドは過去ログ倉庫に格納されています