オーバーライドされた基底クラスメソッドを呼び出す
派生クラスで基底クラスメソッドをオーバーライドされた場合、処理は派生クラス側で上書きされてしまいますが、別に基底クラスメソッドが消えるわけではなくちゃんと呼び出すことが可能です。基底クラスのメソッドを呼ぶ場合はbaseキーワード(VB.NETはMyBase)を使用します。
public class BaseClass { public virtual void ShowMessage() { Console.WriteLine("基底クラス"); } } public class SubClass : BaseClass { public override void ShowMessage() { Console.WriteLine("派生クラス"); base.ShowMessage(); } }
Public Class BaseClass Public Overridable Sub ShowMessage() Console.WriteLine("基底クラス") End Sub End Class Public Class SubClass Inherits BaseClass Public Overrides Sub ShowMessage() Console.WriteLine("派生クラス") MyBase.ShowMessage() End Sub End Class
基底クラス 派生クラス 基底クラス
null許容型
値型がnullを許容しないことは分かりました。しかし、アプリケーション構築上、nullを許容しなければいけない場面もあります。そこで登場したのがnull許容型です。
null許容型は値型の後ろに「?」をつけるだけです。しかし、元々許容型である参照型やstring型は「?」をつけることができません。
int? a = null; decimal? b = null;
Dim a As Integer? = Nothing Dim b As Decimal? = Nothing
null許容型はコンパイルされると、結果的にNullable
int? a = null; Nullable<int> b = null;
Dim a As Integer? = Nothing Dim b As Nullable(Of Integer) = Nothing
CodeBehindによるファイル追加で発生する問題
ASP.NETのプロジェクトファイルに既存ファイルを追加する場合、通常はVisualStudioのソリューションエクスプローラーから追加します。しかし、状況によってはOSのエクスプローラー上でファイルの移動をする場合があります。
この場合、ASP.NETのプロジェクトがCodeFileで作成してあれば特に問題になることはありませんが、CodeBehindで作成している場合は問題になります。CodeFileは動的コンパイルによって構成ファイルをVisualStudioではなく、ビルド、発行時にファイルが含まれません。これはCodeFileが動的コンパイラに対し、CodeBehindはビルド処理によるコードファイルのコンパイラとなっている為です。
つまり、CodeBehindはあらかじめビルドしたアセンブリ(dll)に対してWEBサーバーが応答する形ということです。
これを回避するためには、追加した後に以下の手順でファイルをプロジェクトに含める必要があります。
1. エクスプローラを使って、追加したいフォルダをプロジェクトフォルダの中にコピーする
2. ソリューションエクスプローラで「すべてのファイルを表示」をONにする
3. フォルダが見えると思うので、それを右クリックして「プロジェクトに含める」
null許容型(C#とVB.NETの違い)
参照型はnullを許容していますが、値型はどうでしょうか。実はここでもC#とVB.NETで微妙に挙動が異なります。
C#におけるnull代入時の挙動
参照型はnullを許容していますが、値型はnull非許容です。つまり、以下のような構文はそもそもコンパイルエラーとなってしまいます。
int a = null; decimal b = null;
VB.NETにおけるNothing代入時の挙動
VB.NETのnullはNothingとなります。参照型はC#同様null許容なので何の問題もありませんが、問題は値型です。結論から言えば、C#であろうとVB.NETであろうと値型はnull非許容です。
Dim a As Integer = Nothing Dim b As Decimal = Nothing
しかしコンパイルが通ってしまいます。何故でしょうか。
VB.NETは暗黙的に初期化されてしまう
以下の構文をそのまま実行してみましょう。
Dim a As Integer = Nothing Dim b As Decimal = Nothing Console.WriteLine(a) Console.WriteLine(b)
0 0
つまり、VB.NETの場合null非許容の値型へNothingを代入すると、内部的に初期化される仕様となります。
VB.NETの値型がnull非許容ということは、以下の構文を書くとよく分かります。
Dim a As Integer = Nothing If a Is Nothing Then End If
これは2行目でnull許容に関するコンパイルエラーとなります。
'Is' 演算子は型 'Integer' のオペランドを受け付けません。オペランドは参照型または Null 許容型でなければなりません。
このように、C#とVB.NETで挙動が違い、またVB.NETの場合は非常に分かりにくい仕様になっています。
これの何が問題かというと、オブジェクト指向でデータベースを扱ったりクラスを設計した場合、どうしてもnull(要は何もない状態)を返したい場合が出てきます。C#であれば値型はnull非許容なので明示的にnull許容型(別途説明します)にすれば何の問題もありませんが、VB.NETの場合、例えばInteger型だとNothingを代入しても0が返ってきてしまいます。つまり、Nothing代入による初期値0のことなのか、処理結果の0なのかの判断が出来ないということになります。
this参照
this(VB.NETはMe)が一体どういう意味を持っているのか、どういったときに使用するのかを説明します。
そもそも、ローカル変数とフィールド変数の名前がかぶらなければお目にかかることはないわけですが、オブジェクト指向型言語であれば一度や二度はかぶってしまったことがあるのではないでしょうか。
通常名前がかぶった場合はローカル変数が優先されますが、this参照するとフィールド変数を優先してくれます。
public string naiyo = "piyo"; public void Test() { string naiyo = "hoge"; Console.WriteLine(naiyo); Console.WriteLine(this.naiyo); }
Private naiyo As String = "piyo" Public Sub Test() Dim naiyo As String = "hoge" Console.WriteLine(naiyo) Console.WriteLine(Me.naiyo) End Sub
hoge piyo
別に違う変数名にすればと思うかもしれませんが、オブジェクト指向のクラス設計上やむを得ない時があるのです。
なお、this参照は静的クラスでは使用できません。
オーバーライド
よく間違えるNo1のオーバーライドです。「オーバーロード」とよく間違えて使う人がいますが、この二つ全く意味が違いますので混同しないように注意しましょう。オーバーライドはオブジェクト指向の「継承」分野で必要な機能となります。
オーバーライドを簡単に説明すると、「振る舞いを上から(over)塗りつぶす(ride)」というニュアンスで、基底の振る舞いを継承先で変更する場合に使用します。メソッド、プロパティ、インデクサーに使用でき、変数やstaticメンバー、抽象メソッド(abstract)には使用することは出来ません。
今回はメソッドを例としてオーバーライドを説明します。
オーバーライドされる側のメソッドにC#の場合「virtual」、VB.NETの場合「Overridable」キーワードを指定します。このキーワードを指定しないメソッドはオーバーライドすることが出来ません。
public class BaseClass { public virtual void ShowMessage() { Console.WriteLine("基底クラス"); } }
Public Class BaseClass Public Overridable Sub ShowMessage() Console.WriteLine("基底クラス") End Sub End Class
つまり、派生クラスで勝手に書き換えを禁止する為の措置となります。これらのメソッドを「仮想メソッド」と呼びます。
オーバーライドする側のメソッドはC#の場合「override」、VB.NETの場合「Overrides」キーワードを指定します。このキーワードを指定すると、メソッドを自由に変更することが出来るようになります。
public class SubClass : BaseClass { public override void ShowMessage() { Console.WriteLine("派生クラス"); } }
Public Class SubClass Inherits BaseClass Public Overrides Sub ShowMessage() Console.WriteLine("派生クラス") End Sub End Class
実行は以下のように、派生クラスについては継承元の型でも宣言可能(ポリモーフィズム)です。
BaseClass test1 = new BaseClass(); SubClass1 test2 = new SubClass(); BaseClass test3 = new SubClass(); test1.ShowMessage(); test2.ShowMessage(); test3.ShowMessage();
Dim test1 As New BaseClass() Dim test2 As New SubClass() Dim test3 As BaseClass= New SubClass() test1.ShowMessage() test2.ShowMessage() test3.ShowMessage()
基底クラス 派生クラス 派生クラス
よって、以下のようにジェネリックコレクションを利用した宣言も可能となります。
List<BaseClass> test = new List<BaseClass> { new BaseClass(), new SubClass1() }; foreach(BaseClass t in test) { t.ShowMessage(); }
Dim test As New List(Of BaseClass) From {New BaseClass(), New SubClass1()} For Each t As BaseClass In test t.ShowMessage() Next
ボックス化って何?
型変換でよく出てくる用語の一つに「ボックス化(又はボクシング)」があります。よくボックス化はコストがかかるとか言われますが、一体何の意味か分かりますか?
値型を参照型に変換する行為
簡単に言うとそういうことです。例えばint型をobject型に変換することを「ボックス化」と呼ぶわけです。ただこれは少し乱暴な表現で、冷静に考えればint型は値型でobject型は参照型なので型変換が必要なはずです。なぜ型変換せず変換できるのでしょうか。
int n = 10; object o = n;
Dim n As Integer = 10 Dim o As Object = n
実はint型はobject型の派生なのです。int型に限らず値型はobject型の派生なので、型変換無しでボックス化が暗黙的に(CLRが)行われます。よって我々が意識することはなく内部で自動的に行われますので、気が付かずにボックス化しているなんてことも起こりえます。
ボックス化の逆は明示的に型変換が必要
ボックス化の逆を「ボックス化解除(又はアンボクシング)」と言います。オブジェクトから値型を抽出する行為です。これはボックス化と異なり、型変換を明示的に行う必要があります。なお、Javaの場合はオートボクシング機能がありますので、この部分のみC#とJavaでボックス化のニュアンスが異なりますので注意して下さい。
int n = 10; object o = n; o = 20; n = (int) o;
Dim n As Integer = 10 Dim o As Object = n o = 20 n = CType(o, Integer)
ボックス化(ボックス化解除)はコストがかかります
値型はスタック領域に書かれますが、参照型はヒープ領域に書かれます。当然スタック領域に比べてヒープ領域の方が速度が遅めですので、そこの変換作業によってコストがかかるということになります。
ボックス化
スタック上の値がヒープ上の新しい領域に値が作成され、その参照情報がスタック領域の新しい場所へ格納されます。
ボックス化解除
ヒープ領域に作成したボックス化情報を型変換すると、スタック上に新たな領域を確保して値を作成します。
厳密に型を指定して宣言した方がよい、というのは不用意なボックス化を防ぐ意味でもあります。