佐々木屋

技術的なことから趣味まで色々書きます

MBRディスクのパーティション

Windows7以上であればOSインストール後でもWindows標準のディスク管理からパーティションを作成することは可能です。しかし、MBRディスクの場合はパーティション数の最大が4個というちょっとした制限があります。

それ以上のパーティションを作成しようとすると、ディスクをダイナミックに変換しない限り以下のメッセージが表示されて正しく作成出来ません。

f:id:sasaki816:20190410235019j:plain


MBRディスクには他にも制限があり、2TBまでしか認識しません(所謂2TBの壁)。これを超える容量を認識させる場合は、MBR方式ではなくGPT方式を採用します。

但し、GPTディスクの場合、以下の条件が必須です。

これらの場合、GPTディスクでのOSブートが可能となります。


なお、現時点のディスク方式を調べる方法は、ディスク管理より対象ディスクを右クリック→プロパティで調べると確認することができます。パーティションのスタイル欄に表示されます。
f:id:sasaki816:20190410235037j:plain


MBRからGPTへ移行する場合もディスク管理から行えます。但し、一旦ボリュームを全削除する必要があります。

コンストラクタの継承(引数あり)

継承された派生クラスは基底クラスのメソッドを(protected以上であれば)自由に利用することができますが、コンストラクタは通常のメソッドと異なり、サブクラスで暗黙的に利用できるようにはなりません。

基底クラスでコンストラクタを定義した場合は、派生クラスでも明示的にコンストラクタを定義する必要があります。

public class BaseClass {
    public BaseClass(string naiyo) {
        Console.WriteLine("基底クラス:" + naiyo);
    }
}

public class SubClass : BaseClass {
    public SubClass(string naiyo):base(naiyo) {
        Console.WriteLine("派生クラス:" + naiyo);
    }
}

SubClass cls = new SubClass("hoge");
Public Class BaseClass
    Public Sub New(ByVal naiyo As String)
        Console.WriteLine("基底クラス:" & naiyo)
    End Sub
End Class

Public Class SubClass
    Inherits BaseClass
    Public Sub New(ByVal naiyo As String)
        MyBase.New(naiyo)
        Console.WriteLine("派生クラス:" & naiyo)
    End Sub
End Class

Dim cls As New SubClass("hoge")

要するに、基底クラスで特殊なコンストラクタ(「デフォルトコンストラクタ(引数無しのコンストラクタ)」以外)を定義しているなら、派生クラスでもそれを明示的に書く必要がある、ということです。

ここがダメだよ!VB.NET①

どういうタイトルをつけようか迷いましたが、これが一番しっくりくるかな~?


私は手続き型言語オブジェクト指向言語も関数言語も一通り経験してきましたが、一番好きな言語は何?と聞かれたら、断然C#と答えます。

そしてC#とよく比較される言語の一つとしてVB.NETが挙げられます。この二つは同じフレームワーク上で動作しますので、振る舞いであったり環境であったり様々なところで比較されます。

DelphiC#とかJAVAC#とかならまだしも、縁も所縁もないVB.NETと比較され、VB.NETが少し不憫にも思います。本来VB.NETは粗悪な言語ではありませんし、むしろ初学者でも分かりやすい、親しみやすい優良言語なのですが、VB6の負の遺産から脱却できないことと、その機能を引き継いでしまったこと、親しみやすいが故にプログラマの質が低い等の問題で、どうしてもいいように言われていないのが現状です。

ということで、ここではあまり完全否定をするようなタイトルは付けず、あえて未来志向(そうか?)そうなタイトルをつけてみました。逆に言えば、ここで指摘するダメなところはバグの温床になったり可読性の問題だったりするわけで、それを知っているだけでも綺麗なコードを書く一つの情報になるかもしれません。


前置きが長くなってしまったので、実際のうんちくは次回からです(オイオイ)。

コンストラクタの継承(引数なし)

クラスの継承をすると基底クラスのフィールドやメソッドは(protected以上であれば)派生クラスからでも自由に利用することができますが、コンストラクタは少し挙動が異なります。

引数が無い場合のコンストラクタは派生クラスが呼ばれた時点で自動的に基底クラスのコンストラクタが呼ばれるようになっています。「コンストラクタは継承しない」と書いてあるWEBサイトもありますが、厳密には引数なしコンストラクタは継承されて暗黙的に実行されます。


基底クラスにコンストラクタが定義されている場合、派生クラスでも基底クラスのコンストラクタが自動的に実行されます。順番的には
 基底クラス → 派生クラス → 派生派生クラス・・・
の順番で実行されます。

public class BaseClass {
    public BaseClass() {
        Console.WriteLine("基底クラス");
    }
}

public class SubClass:BaseClass {
    public SubClass() {
        Console.WriteLine("派生クラス");
    }
}

SubClass test = new SubClass();
Public Class BaseClass
    Public Sub New()
        Console.WriteLine("基底クラス")
    End Sub
End Class

Public Class SubClass
    Inherits BaseClass
    Public Sub New()
        Console.WriteLine("派生クラス")
    End Sub
End Class

Dim test As New SubClass()
基底クラス
派生クラス

オプション引数②

オプション引数がC#で長らく導入されてこなった詳しい経緯は不明ですが、私が想像するに「オーバーロードと機能がかぶるから」が一番の理由かなーと思っています。

オプション引数とオーバーロードを組み合わせてしまうと、当然挙動が分かりにくくなりますのでおすすめしませんが、あえてどういう挙動になるかだけ説明しておきます。

オーバーロードの優先順位は以下の通りです。
 オプション引数無し → オプション引数あり → 可変長引数

private void test(int a, int b, int c = 4) {
    Console.WriteLine("オプション引数あり");
}
private void test(int a, int b) {
    Console.WriteLine("オプション引数なし");
}
private void test(params int[] a) {
    Console.WriteLine("可変長引数");
}

test(1, 2);
test(1, 2, 3);
test(1, 2, 3, 4, 5, 6);
Private Sub test(ByVal a As Integer, ByVal b As Integer, Optional ByVal c As Integer = 4)
    Console.WriteLine("オプション引数あり")
End Sub
Private Sub test(ByVal a As Integer, ByVal b As Integer)
    Console.WriteLine("オプション引数なし")
End Sub
Private Sub test(ParamArray c As Integer())
    Console.WriteLine("可変長引数")
End Sub

test(1, 2)
test(1, 2, 3)
test(1, 2, 3, 4, 5, 6)
オプション引数なし
オプション引数あり
可変長引数

インターフェース

クラスの特定機能を保証(強制)する仕組みをインターフェースと言います。

恐らく一番有名なインターフェースといえばIDisposableインターフェースですね。IDisposableはDisposeの実装が約束されますので、クラスを呼ぶ場合にusingステートメントが利用できるようになる(リソース解放が確約されているので)わけです。

インターフェースの制約

通常クラスのメソッドであれば規則と内容両方を実装しますが、インターフェースは規則のみを実装します。つまり、約束事だけを表現するようなクラスとなります。その点では抽象クラスに非常ににています。

インターフェースで定義するメソッドやプロパティはアクセス修飾子はつけることが出来ません。というより、定義したメンバーは全て自動的にpublic abstract(VB.NETの場合はPublic MustOverride)となります。

他に、メンバー変数や静的メソッドは定義出来ないなどの制約があります。

また、インターフェースを定義した派生クラスでは、必ずインターフェースメソッドを利用する必要があります。

インターフェースの実装

C#の場合は継承と同様でクラス名の後に「:」を付けてそのままインターフェースを定義します。

interface IClass {
    void ShowMessage();
}
public class Class1 : IClass {
    public void ShowMessage() {
        Console.WriteLine("派生クラス");
    }
}

VB.NETの場合C#と少し実装方法が異なります。Implementsキーワードで実装するクラス名の後に定義しますが、メソッドも同じようにImplementsキーワードとインターフェースで定義したメソッド名を定義する必要があります。

Interface IClass
    Sub ShowMessage()
End Interface
Public Class Class1 : Implements IClass
    Public Sub ShowMessage() Implements IClass.ShowMessage
        Console.WriteLine("派生クラス")
    End Sub
End Class

VB.NETのみ、派生クラスのメソッド名は別名を使用することができます。

Public Class Class1 : Implements IClass
    Public Sub HogeMessage() Implements IClass.ShowMessage
        Console.WriteLine("派生クラス")
    End Sub
End Class

ポリモーフィズム(継承)

継承を利用したポリモーフィズムの実現です。

デリゲートの場合はクラス内でも別クラスでも使えますが、継承は別クラスの場合に有効です。しかもデリゲートに比べると普段のクラス宣言と同じように使用出来ることや特別な記法は不要なので、特にVB.NETな方は容易に理解できると思います。


「クラス1」と表示されるShowMessageメソッドがあるクラスを考えます。このメソッドと同じような機能として、「クラス2」と「クラス3」と表示されるクラスもそれぞれ作成したとしましょう。以下の通りです。

public class Class1 {
    public void ShowMessage() {
        Console.WriteLine("クラス1");
    }
}
public class Class2 {
    public void ShowMessage() {
        Console.WriteLine("クラス2");
    }
}
public class Class3 {
    public void ShowMessage() {
        Console.WriteLine("クラス3");
    }
}
Public Class Class1 
    Public Sub ShowMessage()
        Console.WriteLine("クラス1")
    End Sub
End Class
Public Class Class2
    Public Sub ShowMessage()
        Console.WriteLine("クラス2")
    End Sub
End Class
Public Class Class3 
    Public Sub ShowMessage()
        Console.WriteLine("クラス3")
    End Sub
End Class

これらのメソッドを全て実行しようとした場合、呼び出し側では以下のようにインスタンスを別々に作成して、別々にメソッドを実行する必要があります。

Class1 test1 = new Class1();
Class2 test2 = new Class2();
Class3 test3 = new Class3();

test1.ShowMessage();
test2.ShowMessage();
test3.ShowMessage();
Dim test1 As New Class1()
Dim test2 As New Class2()
Dim test3 As New Class3()

test1.ShowMessage()
test2.ShowMessage()
test3.ShowMessage()



これらのクラスは機能・構造としては全く一緒で、違う部分は表示される内容、即ちメソッドの内容だけとなります。そうした場合Class1を基底クラスとして継承し、派生クラスをClass2、Class3とすればよいことになります。

しかし、メソッドを継承した場合、基底クラスのメソッドが実行されてしまいますので、それでは全て「クラス1」と表示されてしまいます。というワケでShowMessageメソッドをオーバーライドして新しく書き直せばいいということになります。

public class Class1 {
    public virtual void ShowMessage() {
        Console.WriteLine("クラス1");
    }
}
public class Class2 : Class1 {
    public override void ShowMessage() {
        Console.WriteLine("クラス2");
    }
}
public class Class3 :Class1  {
    public override void ShowMessage() {
        Console.WriteLine("クラス3");
    }
}
Public Class Class1 
    Public Overridable Sub ShowMessage()
        Console.WriteLine("クラス1")
    End Sub
End Class
Public Class Class2
    Inherits Class1 
    Public Overrides Sub ShowMessage()
        Console.WriteLine("クラス2")
    End Sub
End Class
Public Class Class3
    Inherits Class1 
    Public Overrides Sub ShowMessage()
        Console.WriteLine("クラス3")
    End Sub
End Class



すると呼び出し側は以下のようにスマートに書けるようになります。

List<Class1> test = new List<Class1> { new Class1(), new Class2(), new Class3() };
foreach(BaseClass t in test) {
    t.ShowMessage();
}
Dim test As New List(Of BaseClass) From {New BaseClass, New SubClass1, New SubClass2}
For Each t As BaseClass In test
    t.ShowMessage()
Next

メソッド実行がt.ShowMessage()のひとつになりました。これがポリモーフィズムの正体です。