ブルーノのC#プログラミング & unity勉強日記

プログラミング素人、ブルーノの自主勉強ノートです。他のプログラミングを勉強したい方の助けになれば幸いです。その他趣味の雑記もしていきたいです

型パラメータの制約について

f:id:covory10101101276:20180118113042j:image

藤園

 

みなさんこんにちは🌚

爽健美茶と玄米茶が定期的に飲みたくなるブルーノです。あんまり私は飲み物を飲まないんですがたくさん飲んだ方が健康には良いらしいですね。

さて今回はジェネリッククラスの型パラメータの制約の仕方についてまとめていきます。私は制約と聞くとクラピカを連想しますね(どうでもいい)

 

型パラメータの制約とは?

プログラムは他人にも公開したり、別の機会に一部だけ使ったりすることができるのが利点です。ジェネリッククラスでもこれを行うことはできるのですが、この型パラメータ<T>にユーザーが指定したものを勝手に指定されると不具合が起こることもあります。少なくとも自分が安全を確認したものだけを使えるようにできれば一番良いですよね。そこでこの型パラメータに制約を設けることができます。今回はwhereキーワードとnew()制約を学んでいきます。

 

whereキーワード

whereキーワードを使うと指定したクラス名型以外は型パラメータに設定できなくなります。以下の様に書きます

class ジェネリッククラス名<T> where T: クラス名

 これで型パラメータ<T>に入れられるのは「クラス名」の型のみになります。

 

new()

new()制約は先ほどのwhereと合わせて使い、これを設けるとそのジェネリッククラスの型パラメータには引数のないコンストラクタを持っている必要があるようになります。書き方としては以下の様になります

class ジェネリッククラス名<T> where T: new()

 複数の制約を組み合わせることもでき、その場合は以下の様に書きます。

class ジェネリッククラス名<T> where T: クラス名, new()

例えばこうすれば指定したクラス名にはコンストラクタが必要になります。

ではこれらを実装したプログラムを作ってみます

f:id:covory10101101276:20180119095939p:imagef:id:covory10101101276:20180119095942p:imagef:id:covory10101101276:20180119095943p:image

14行目でBruno115クラスのみを型パラメータに設定でき、なおかつそれにコンストラクタを持つように制約をかけました。new()制約をかけているおかげでで16行目ではコンストラクタを持つことが保証されているT(Bruno115クラス) をそのままインスタンス化できています。

他にも制約は色々あり、参照型のみを型パラメータにできるclassキーワード値型のみを型パラメータにできるstructキーワードなど色々あります。うまく使い分けて安全なプログラムを組める様にしたいですね。今回はここまでにしておきます。

 

プログラミングを勉強していると単純な機能拡張の仕組みと同じくらい今回のような安全性を高める仕組みが見受けられます。それほどプログラムの安全性を高めることは大切なんだなあと思いました。

ところで新年初のおみくじが吉でした。大吉に次いで良い目のはずなのに原付がパンクしたり親戚の病気が急変したり今のところ全然いいことがありません😅一年の平均が吉だとしたらこれから良いことがバンバン起こる予定なので楽しみにしてます笑

Listクラスについて&ジェネリックの継承

f:id:covory10101101276:20180117111918j:image

みなさんこんにちは🌚

最近自炊をよくするブルーノです。実は炭水化物を極力取らないようにしていて、米をもう三ヶ月以上食べていません。でもそれだと意外に簡単だったので先週くらいからパン・麺・芋も縛るようにしました。一体何を目指しているんでしょうね俺は笑笑

では今回は二本立てでやって行きたいと思います。

 

Listクラス

だいぶ前にArrayListクラスについてやりました。ArrayListクラスについて(既存のクラス) - ブルーノのC#プログラミング & unity勉強日記

配列のように複数の要素を持つクラスを作るこのクラスですが実はジェネリックにもこれに似たものがあります。それがListクラスです。ジェネリックなのでこちらは明示的に型を指定することでその型として使うことができます。Listクラスのプロパティには格納されている要素の個数を表すCountや、指定のインデックスの要素を取得、設定できるItemなどがあり、関連するメソッドには、Listの末尾にオブジェクトを新たに追加できるAdd()や並び替えのSort()があります

ListクラスはSystem.Collections.Generic名前空間で定義されているのでプログラムの冒頭で宣言する必要があります。では実際にプログラムを書いてみます

f:id:covory10101101276:20180117115804p:imagef:id:covory10101101276:20180117115806p:imagef:id:covory10101101276:20180117115810p:image

2行目にusing.System.Collections.Generic; が新しく追加されているのがわかりますね。

9行目でリストの宣言をしています。実はこのリストにデータを格納する場合、実はいちいちAdd()メソッドで追加しなくてもここの段階を以下のようにするとこの行だけで初期化することができるので覚えておきましょう

List<int> i = new List<int> { 1, 10, 100....};

 

ジェネリックの継承

さて次はジェネリッククラスが継承できるのかということについてやっていきます。

通常、ジェネリッククラスは型パラメータを指定して使いますよね?この型パラメータを指定したジェネリッククラスをクローズ構築型といい、指定されていないものをオープン構築型と言います。ん?指定されてないもの?なんだそれ?ってなるので実際にプログラムを作った方が早そうです

 f:id:covory10101101276:20180117142359p:imagef:id:covory10101101276:20180117143325p:image

注目すべきは8行目で、Bruno107クラスをBruno106ジェネリッククラスから継承しています。この際Bruno106ジェネリッククラスはint型を指定してますね。これがクローズ構築型のジェネリッククラスで、普通のクラスに継承できるのはこのタイプになります。ではもう一つ見ていきます。

f:id:covory10101101276:20180117144024p:imagef:id:covory10101101276:20180117144026p:image

8行目にあるように、今度は

 Bruno110ジェネリッククラスをBruno109ジェネリッククラスから継承しています。その結果、メインメソッドでは同じBruno110クラスにそれぞれ別の型を指定して表示していますね。このように型パラメータを指定しないで通常メソッドから継承しようとした場合はコンパイルエラーになってしまいます。ところでゴリラのスペルってGorrilaじゃなくてGorrilaなんですよね。びっくりしたウホ。

 

まとめてみると以下のようになります

通常クラス

通常クラス → 継承できる

クローズ構築型ジェネリッククラス → 継承できる

オープン構築型ジェネリッククラス → 継承できない

 

ジェネリッククラス

通常クラス → 継承できる

クローズ構築型ジェネリッククラス → 継承できる

オープン構築型ジェネリッククラス → 継承できる

 

触れていませんでしたがジェネリッククラスは通常のクラスからも継承できます。ん?じゃあ二つの型パラメータを持つジェネリッククラスを継承するときはどうするの?ということですが、まず通常クラスにはオープン構築型ジェネリッククラスは継承できず、全て継承元は型パラメータを明示しなければならないのであまり疑問にはなりませんね。ではジェネリッククラスの場合についてですが、両方の型パラメータを継承する場合は問題ないのですが、一部だけを継承する場合はそれ以外の部分を明示しておかなければなりません。どういうことか、それも見てもらったほうが早いですね

f:id:covory10101101276:20180117220946p:plain

f:id:covory10101101276:20180117220955p:plain

9行目のように、継承しない場合はこの時点で明治しておく必要があります。ちょっと多くなってしまいましたがここで今回は終わりにしたいと思います。

 

最近朝の寒さがあまり厳しくなくなってきて気づいたら暦の上では実は冬至を過ぎていたのを知りました。まあまだ2月も待ち受けているんですが無事今年も冬を越せそうです。

ジェネリックの基礎について

f:id:covory10101101276:20171228124905j:image

みなさんこんにちは🌚

冬休みも課題や集中講義に追われているブルーノです。課題の合間にはこのブログで息抜きがてら勉強します。まあゲームもやってるんだけどね。

冬休みにまとめきれなかったー😭

さて今回からはジェネリックというものについて勉強していきます

 

ジェネリック

名前を見ると私は黒柳徹子しか浮かびませんがこのジェネリックという概念はC#ではかなり強い(有用である)そうです。ジェネリック(generic)とは英語で「ブランドにとらわれない」とか、「一般的な」という意味で、ジェネリック医薬品は万人に効果があると証明されたものの名前だそうです。

C#でも同じで、型にとらわれないというのがこのジェネリッククラスの特徴です。

ジェネリッククラスはそのインスタンス生成時にそのメンバの型を決定します。書き方としては以下のようになります。

class MyClass<T>

{

  public T x;

  public int show(T p) {...}

}

クラス名の横やメンバの型がTというものに置き換わっていますね。このTは自分で指定したもので別のアルファベットや単語でも構わないです(一般的には大文字1文字で表されるようです)。このTを型パラメータといい以下のようにインスタンス生成して使います

MyClass<int> mc = new MyClass<int>()

この場合は上のTがint型になります。簡単にコードを書いてみます。

f:id:covory10101101276:20180110094907p:imagef:id:covory10101101276:20180110094908p:imagef:id:covory10101101276:20180110094911p:image

Bruno102クラスのメインメソッドで二つのBruno101クラスのインスタンスを生成しています。しかしその中身はそれぞれint型とstring型の別の型を代入できていますね。これこそがジェネリックの強みで、同じ設計図で型違いの物を全てひとまとめにできるのです。

型パラメータは複数取ることもできます。

f:id:covory10101101276:20180117105327p:imagef:id:covory10101101276:20180117105348p:imagef:id:covory10101101276:20180117105352p:imagef:id:covory10101101276:20180117105353p:imagef:id:covory10101101276:20180117105438p:image

少し長くなってしまいましたが二つの配列にそれぞれ番号と名前を保存し、それを表示するプログラムを組んでみました。このようにジェネリックは予め決めておいた設計図にあらゆる型を当てて柔軟に対応させることができるんですね。今回はここまでにしておきます。

 

テストに課題にとても忙しくて全然更新できてません〜〜

でも今月中にはC#の内容を一通り終えたいので頑張ります!

海鮮キムチ鍋家で作ったけどうまかった😍

 

新年度!!

f:id:covory10101101276:20180109111318j:image

みなさんあけましておめでとうございます🌅🌚

 

冬休みは忙しくてちっともブログの方が更新できていませんでした。今使っている「猫でも分かる」の本もだいぶ終盤にさしかかってきているのでこれを終わらせて新年をunityの使い方の勉強で始めたかったのですが間に合いませんでしたね😡😭スミマセン

新年の抱負をここに書き留めますがその前に自分のことについて振り返らせてください。ちょっとネガティブな入りですが....

私は昔からあまり意志が強くなく、勉強でも仕事でも苦手意識がある物はことごとく続かなかったです。楽しいことだけを追って逃げてきました。でも大学に来て、それじゃあ現実は生きて行けないんだという事を今更ですが痛感しました。親元を離れて初めて自分がいかに甘えて生きてきたかが分かりました。ああもっと嫌なことにも中高時代に取り組んでおけばよかったと典型的ですが後悔しました。

最近ようやく苦手でも自分のためにやらなきゃいけないことへの姿勢が少しづつですが付いてきた気がします。まあ今まで逃げてきた私がいきなりちょっと頑張ったところで今まで頑張ってきた人たちと同様に動けるわけもなく、中学の時の親友と年始に会った時に年末年始の勉強の話をしたらできないことはないだろと言われてしまいましたが笑

そいつは私が最も尊敬する人の一人で、中学の時の部活が一緒でした。彼は教室で見るたびに勉強していて遊びに誘っても来ることはなく、でも行事の節々は全力で楽しんでいて人付き合いが良かったです。彼は今は早稲田大学に行っており、話を聞くといずれ院にも行きたいそうです。私はときおり彼に会ってモチベーションをあげさせてもらっており、今回もまた勝手に喝を入れてもらいました。

話が逸れましたが、社会的関心と意識を持って社会性を身につけなければこの社会でできることは限られてしまうという当たり前の理をようやく自分の中で持つことができかけてきたということがこの前フリで明言しておきたいことです。

今私はヒーヒー言って必死に現実にしがみついています。そしてその中で、今までは若干現実逃避のためにプログラミングを勉強していましたが、今は本当に自分の将来のためにスキルを身に付けたいと思っています。なので今年中にアプリを作って売り出すということを今年の抱負にさせていただきます。

 

長くなってしまいましたが自分用なので今の思いを忘れないようにしたいです。

新年も頑張るぞᕦ(ò_óˇ)ᕤ

演算子のオーバーロード

f:id:covory10101101276:20171224183114p:plain

みなさんこんにちは🌚

久々に東京に出向き、FF7ACのウォールスクロールを衝動買いしてしまったブルーノです。クラウドのウォールスクロールなのですが思ったよりでかくて家の中で存在感を放っています。

さて今回は演算子オーバーロードというものについてやっていきます。一回にまとめたら多くなってしまいました。

 

演算子オーバーロードとは?

演算子については以前最初の方でさらっとだけ紹介しました。しかし当時は記号を見てわかる通りの算数でも習うような基本的な機能しか使わなかったので本当に一瞬しか触れませんでした。実はその演算子の定義をクラスの時のようにオーバーロード(複数定義)させることができるのです。私自身も全てを理解したわけではありませんが、どうやら演算子の機能の拡張のために作られたもののようです。

例えば、 + の演算子はこれまで数値や文字列を合わせるためだけに使用してきましたが、その定義をオーバーロードして変えることで、違う型同士を足したり、オブジェクト同士の足し算の中身を自由にカスタマイズしてスムーズに処理できます。しかしC#の文法上のルールを変えることはできないのでこの変更はできるだけ元の演算子の振る舞いに似たものにする必要があります。というよりあまり演算子オーバーロード自体しない方が良いらしいです。

演算子オーバーロードを行う際にはまず、そのオーバーロードした演算子を使いたいクラス内部で定義する必要があります。そして

public static 戻り値の型 operator 定義する演算子 (引数リスト)

とメソッドのように定義します。この戻り値の型というのは一般的に定義を変更するクラス名です。実際に例を書き起こしてみます。

下の画像は複素数の計算の様子をプログラムに表したものです。複素数はざっくり説明すると虚数を含んだ数のことで、虚数とは存在しない数のことです。虚数複素数は高校で習う単語なのでそれが分からない場合は存在しない数とだけ今回は覚えてください。この存在しない数、存在がないため他の数とごっちゃにして計算してはまずいので、それを含む数の中でも常に「 i (imaginary)」という文字で孤立しています。つまりX という複素数の中でもa + biというように中身が別れており、このaが実部(real)、biが虚部(imaginary)と呼ばれています。複素数Xと、別の複素数を計算する際、いちいち分解することもできますが、+演算子一つにこの実部同士、虚部同士の計算をするように定義すると計算が楽ですよね。これが機能の拡張というもので、こういう場合にオーバーロードを使用します。さて、長くなってしまいましたがコードの説明に移ります。

単項演算子の計算について

f:id:covory10101101276:20171224202737p:plain

f:id:covory10101101276:20171224202742p:plain

f:id:covory10101101276:20171224202747p:plain

f:id:covory10101101276:20171224202753p:plain

まず単項演算子についてですが、60,61行目にあるように(-A,-Bのこと)、項が一つだけの計算の時の演算子のことです。1枚目が実部と虚部の準備、2枚目が肝心のオーバーロードの中身について、3枚目がその計算の様子を表したものです。注目すべきは2枚目で、Bruno092から継承したBruno093クラスにオーバーロードを実装しています。複素数にマイナス( - )がかけられた場合、実部、虚部にそれぞれマイナスをかけるようになっています。どちらか一つだけにマイナスをかけると計算結果が変わってしまいますよね。

さて、そうして3枚目でこれを使用したBruno094クラスは、びっくりするほど見やすいスッキリしたものになっていますね。60,61行目でマイナスをかけた時の計算を綺麗に処理しています。これが単項演算子オーバーロードです。

2項演算子オーバーロードについて

先ほどは単項演算子オーバーロードだったのでいわゆる「++」とか「-」のみの項が一つの計算で使うものでしたがやはり2項を伴う演算子の方が一般的ですよね。こちらも書き方の基本形は単項の時とあまり変わりません。

public static 戻り値の型 operator 演算子(データ型 オペランド1, データ型, オペランド2)

引数が2個に増え、これにより内部で扱える数が2つに増えました。後は先ほどとあまり変わりません。

f:id:covory10101101276:20171224220048p:plain

f:id:covory10101101276:20171224220057p:plain

一つだけ違うのは、オーバーロードするメソッド内でTostringメソッドについても定義していることです。これをオーバーライドすることで改めてどうやって文字列で表すかを指定することができます。やはりBruno097クラスはスッキリしていますね。

等価演算子について

最後に注意するのは等価演算子についてです。等価演算子とは、「==」や「!=」などのtrue, falseを返す演算子ですね。なぜ注意しなければならないのかというと、先ほどの2項演算子の時にTostringメソッドを定義しておく必要があったように、等価演算子の場合は演算子の定義以外にも定義しなければならないことが3つもあるからです。下の項目がその3つになります。

-Equalsメソッド

このメソッドは自身のオブジェクトと引数のオブジェクトが等しい際にtrueを返し、そうでない場合はfalseを返すメソッドです。

-GetHushCodeメソッド

これが厄介なもので、オブジェクトに対してあるint型の値を割り振るメソッドです。同じオブジェクトは同じハッシュ値を、異なるオブジェクトの場合は異なるハッシュ値を持つようにしなければなりません。これがなんの役にたつかはわかりにくいですが、このint型の一つの数によって、正否や優劣などのオブジェクト同士の整数での比較ができるのです。

-不等価演算子

これはいわゆる等価演算子の逆で、オブジェクト同士が同じ場合はfalse,異なる場合はtrueを返します。つまり先ほどまでの逆を定義すれば良いわけですね。

f:id:covory10101101276:20171224230709p:plain

f:id:covory10101101276:20171224230714p:plain

f:id:covory10101101276:20171224230717p:plain

一枚目のEqualsメソッドで、GetTypeメソッドで取得した型を比較し、型とその中身が等しい場合はtrue,異なる場合はfalseを返します。GetHushCodeメソッドの中身についてですが、内側のGetHushCodeメソッドでハッシュ値を作り、それを複雑にして返している、とだけ覚えておきます。これで結果を見ればわかるように複素数の比較ができていますね。あ、Cの値を表示し忘れていました。コードを見ればBとCが同じことはわかるので許してください。。。

今日はここまでにしておきます。

 

いやあ今回の内容を読み解くのにまた数日を要してしまいました。やはりわからなくてもまずは実際にやってみることが大事ですね。この冬休み中が追い込みなのでがんばって勉強していきたいです😤😡

例外について(4/3)

f:id:covory10101101276:20171222135010j:image

みなさんこんにちは🌚

前回勢い余って内容が多くなってしまい、3回で終われなかったブルーノです。今回は本当に最後のまとめとして例外の他の例をまとめます!

 

独自の例外

 (1/3)回目で出たExceptionクラスやApplicationExceptionクラスからオリジナルの例外を派生させることができます(もちろん他の例外クラスからも派生させられますが、シールされているものもあるので注意してください)。

f:id:covory10101101276:20171222145218p:imagef:id:covory10101101276:20171222144835p:imagef:id:covory10101101276:20171222145225p:image

3~12行目のBruno089派生クラスをDivideByZeroExceptionから派生させ、その内容をnewキーワードやoverrideキーワードで上書きして新しい例外クラスにしています。

次のBruno090クラスでは、その例外で書き換えた部分をキャッチブロックで全て表示するようにしています。ちなみにURLはディシディアファイナルファンタジーNTのβ版のサイトです。是非みなさんやりましょう😀😀

 

オーバーフローについて

 プログラム実行時に変数のデータ型の範囲を超える値が代入されてしまうとオーバーフロー(桁あふれ)が起きてしまい、計算途中でこれが起こることで結果が大幅に狂ってしまいます。この時、checkedキーワードを使うことでオーバーフロー時にOverflowException例外を発生させることができます。checkedキーワードはtry-catch文のようにオーバーフローが起きそうな文を { } で囲むことで使うことができます。

f:id:covory10101101276:20171222232909p:plain

f:id:covory10101101276:20171222232917p:plain

checked文の内側ではオーバーフローを起こすように簡単な式が組まれていて、これによりchecked文はキャッチ節でキャッチされます。ちなみにchecked文の逆でunchecked文というものもあり、これはオーバーフローを無視します。

今回はここまでにしておきます。

 

ついに例外が終了しました。次回からは演算子オーバーロードについてやっていきたいと思います。

今日学校でVBAのゲームを作るよう課題を出されました。C#でもゲームのためにこうして勉強してるのにVBAでそんな高度なことをいきなりやらされるとは.....😓

例外について(3/3)

f:id:covory10101101276:20171220221939p:plain

みなさんこんにちは🌚

最近プログラミング勉強したい欲が高まっているのに学校が忙しくてできないブルーノです😹冬休みに向けて頑張ってスキマ時間を見つけてやっていきます!!というわけで前回の例外処理の様々な他の例をまとめていきます。

 

throw文

 あるtry-catchブロックが含まれているメソッドの中で別のtry-catchブロックが含まれているメソッドを呼び出したい時、内側の例外を外側に投げることができます。これを文字のままスロー(throw)と言います。投げるにはオブジェクト化しなければなりません。ちょっと実感が湧きづらいので例のスクショをあげます

f:id:covory10101101276:20171222112644p:imagef:id:covory10101101276:20171222112644p:image

まず3~19行目の行では配列の境界を越えたり0で割ろうとしたりわざと例外を発生させるように作られたCalcメソッドを設けています。まずここで配列を超えると12行目のIndexOutOfRangeException例外を捕まえるキャッチブロックに捕まります。するとそのメッセージを表示した後にDivideByZeroException例外をオブジェクト化し(14行目)、外側にスローするようになっています(16行目)。そしてこの3~19行目のメソッドを呼び出すのが次の21~34行目のBruno087クラスです。このメソッドは先ほどの一連を呼び出すだけのtryブロックで、キャッチブロックにはDivideByZeroExceptionをキャッチするようになっています。そう、先ほどスローするよう組まれたDivideByZeroExceptionはここでキャッチされ、またその旨のメッセージを表示します。そして最後にこのクラスをメインメソッドから実行しています。つまり、自分で例外を作成し、外側に投げているわけですね。ということはこのプログラムの3~19行目の中で0の除算を行わなくても

f:id:covory10101101276:20171222114216p:imagef:id:covory10101101276:20171222114218p:imagef:id:covory10101101276:20171222114222p:image

このようにDivideByZeroExceptionは投げられ、キャッチされるのです。ちょっとメソッドを別々に組んでいてわかりにくいですね。なので今度はこれをforループの時のようにネストしていまいましょう。

 

try-catch節のネスト

f:id:covory10101101276:20171222133053p:imagef:id:covory10101101276:20171222133056p:image

今度は発生させる例外をOutOfRangeExceptionのみにし、DivideByZeroExceptionとOutOfRangeExceptionの両方のキャッチブロックを張りました。最初のDivideByZeroExceptionのキャッチブロックには当然ですが引っかからず、するとそれが外側にあるOutOfRangeExceptionのキャッチブロックにキャッチされます。このようにtry-catch節をネストさせることもできます。

ただし、同時に例外が複数起こるようCalcメソッド内に設けても最初の例外でキャッチ節に移動してしまうのでそれ以降は実行されないようです。

今回はここまでにします。

 

全3回にして分けるつもりが今回ちょっと長くなってしまいました。あと少しだけまとめることが残っているのでちょっと次回も例外についてまとめます。3回で終わらなかったけどゴメンネ!