昨日、抽象クラスであるところのSystem.IO.Streamを継承してみていろいろやってみました。
class rndstream : System.IO.Stream {
private Random myrnd;
public rndstream() {
myrnd=new Random();
}
public override bool CanRead { get { return true; } }
public override int Read(byte[] buffer, int offset, int count) {
for (int i=0; i<count; i++) {
buffer[offset+i]=(byte)myrnd.Next();
}
return count;
}
public override bool CanWrite { get { return false; } }
public override void Write(byte[] buffer, int offset, int count) { throw new System.NotSupportedException(); }
public override void Flush() { throw new System.IO.IOException(); }
public override bool CanSeek { get { return false; } }
public override long Length { get { throw new System.NotSupportedException(); } }
public override long Position {
get { throw new System.NotSupportedException(); }
set { throw new System.NotSupportedException(); }
}
public override long Seek(long offset, System.IO.SeekOrigin origin) { throw new System.NotSupportedException(); }
public override void SetLength(long value) { throw new System.NotSupportedException(); }
}
なんか例外をthrowばっかりしてるな。
まともに実装したのはコンストラクタとReadメソッドだけだな。
で、抽象クラスというのは、抽象メソッド、抽象プロパティのあるクラスのことだな。
特徴はインスタンスを作れないこと。もっぱら継承して使うクラスだと。
Streamを継承したクラスで有名なのは、System.IO.FileStreamではないかな。
まぁそんなものを自作してみたということです。
で、ひたすらランダムな値を返すというものです。
で読み込み専用なので、CanWriteはfalse一定、WriteメソッドはNotSupportedExceptionをthrow、
シーク禁止なので、CanSeekはfalse一定、シーク関係はひたすらNotSupportedException。
というわけでまともに実装したReadメソッドは見ての通り。
で、これで実際に他のStream同様に取り扱ったところきちんと動いた。
ところで、抽象クラスとインターフェースの違いってなんだろうなという話はありますね。
どうも抽象クラスは抽象メソッド、抽象プロパティだけからなるわけではなくて、
きちんとそうでないメソッドやプロパティもあるらしい。
たとえば、ReadByteメソッドは特にoverrideして定めなくてもうまく動いてくれる。
これは多分Readメソッドを利用して動いてるんだろうね。
まぁだから抽象クラスというのは穴埋めしないといけないクラスのテンプレートなのかな。
インターフェースはそんなことはできないからね。
反面インターフェースは多重継承できるから、それは特徴だよな。
関係ないことなのだが、仮想メソッドと仮想プロパティというものもあるらしい。
これはoverrideしてもいいメソッドやらプロパティという意味なのだが、はてどんな使い道なのやら。
ある例では、インスタンスの年齢を返すプロパティが仮想プロパティで、
そのクラスを継承したクラスでは、そのプロパティをさばを読むようなやつでoverrideした例があった。
で、newを付けるときと何が違うのかという話なのだが、
class foo{
protected int _age;
public foo(int age0){ _age=age0; }
public virtual int age{ get{ return _age; } }
public int agetimes2(){ return age*2; }
}
class foo2 : foo{
public foo2(int age0):base(age0){ }
public override int age{ get{ return _age-1; } }
}
class foo3 : foo{
public foo3(int age0):base(age0){ }
public new int age{ get{return _age-1; } }
}
といろいろクラスを用意して、その上で
foo2 myfoo2 = new foo2(10);
foo3 myfoo3 = new foo3(10);
Console.WriteLine("{0} {1} {2} {3}",myfoo2.age,((foo)myfoo2).age,myfoo3.age,((foo)myfoo3).age);
Console.WriteLine("{0} {1}",myfoo2.agetimes2() , ((foo)myfoo2).agetimes2() );
Console.WriteLine("{0} {1}",myfoo3.agetimes2() , ((foo)myfoo3).agetimes2() );
とすると、多分、「9 9 9 10」と表示されるはず。
virturlと書いたメソッド・プロパティに、派生クラスでoverrideでやると、元のやつを上書きしてしまって、
そして、次のagetimes2メソッドは、そのoverrideされたageプロパティを使ってやっているのでどちらも18と出てくるわけだ。
newは元のやつは上書きしないと。ただ、自分のクラスでは、元のクラスのそのメソッドを覆い隠すだけ。
だからキャストしてやるともとの動作をしたと。
そして、agetimes2メソッドは、fooクラスでの動作を考えるのだから、
newで上書きしたところで、特に値は変わらず、どちらも20と出てくると。
そんなテストでした。
newで上書きしても、元のクラスで定義された動作に影響は与えないし、元のクラスの動作を動かしたければキャストすればいいよということだな。
まぁしかし、仮想メソッドってなんの有用性があるのかねぇ。よくわからん。
以前は抽象クラスって何をしたいんだと思ってたし、
その前はインターフェースって何する物なんだと思ってたんだから大きな進歩やわな。