コーディングルール
ある程度コードの法則を自分で決めてソースを書くとわかりやすくなる。
今後、以下のようにルールを決めてコードを書く。
- インターフェイスには頭に「I」をつける。
- プロパティは頭文字を大文字にする。
- 変数の頭文字は小文字にする。変数名内の単語の区切りなどは見やすく大文字にしても良い。
- グローバル変数の頭にはgをつける。
配列
string[] test = { "abc", "def", "ghi" };
MessageBox.Show(test.Count().ToString());
C#では配列はobjectを継承していて、メンバー変数が利用できる。この場合count()やToString()等。
これらを利用すると便利。
インターフェイスと抽象化クラス(interfaceとabstract)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
interface IPerson
{
//インターフェイス内では「中身の無い関数」を宣言できる
void Initialize();
//public string InterfaceNormalMethod()
//{
// return "インターフェイス内ではメンバを定義できない。つまり変数や「中身のある関数」等を埋め込むことは不可能";
//}
}
//クラスの修飾子にabstractを使うと「中身のない関数」を宣言できるようになる。それ以外は通常のクラスと同じ機能である
//この中身のない関数は継承先で「必ずoverrideで実装する必要がある」ようになる(実装しなければエラーになる)
//反対に言えばabstractクラスはoveerrideする必要がある関数が高確率で含まれている事を他者に知らせている事なる
//(必ずabstract関数を用意しないとエラーになるという事は無い)
//また安全装置としてabstarctクラスはいきなりインスタンスを作成する事は出来ない
public abstract class Person:IPerson
{
//インターフェイスに設定されている関数はインターフェイスを継承したクラス内で「必ず」実装しないとエラーになる
//また、この例の場合Taroで実装してもエラーになるので注意
public void Initialize()
{
int a;
}
//これが中身の無いabstract(抽象)関数。この関数は派生先で必ずoverrideする必要がある。つまりinterfaceと同機能を持つことになる
//このようにabstractクラス内にabstractメンバ関数を作成するとインターフェイスになる。C#ではinterface使った方がスマートであるが
//interfaceクラスの中でvirtualメンバ関数は作れないのでここはクラスの相互関係の設計の際、一考に値する
public abstract string AbstractGetName();
public virtual string GetName()
{
return "私の名前は決まっていません";
}
public string NormalMethod()
{
return "通常関数をabstractクラス内に埋め込むことは可能";
}
}
public class Taro : Person
{
//このように継承した先で実装する
public override string AbstractGetName()
{
return "抽象化した関数をセット";
}
}
class Program
{
static void Main(string[] args)
{
Taro taro = new Taro();
//Person test = new Person(); //このように抽象クラスのインスタンスは作成できない
Console.WriteLine(taro.GetName());
Console.WriteLine(taro.NormalMethod());
}
}
//このことから判るようにabstractクラスで宣言したabstract関数はインターフェイスとそっくりの機能となる
}
IEnumerable(foreach対応)とIEnumrator(列挙機能)の実装
<定義側>
class MyEnumerator : IEnumerator
{
int pointer;
Vector2[] target;
object IEnumerator.Current //IEnumeratorの3つの関数を実装していく
{
get { return target[pointer]; }
}
bool IEnumerator.MoveNext()
{
if (pointer >= target.GetUpperBound(0)) return false;
pointer++;
return true;
}
void IEnumerator.Reset()
{
pointer = target.GetLowerBound(0) - 1;
}
public MyEnumerator(Vector2[] array) //コンストラクタで初期化
{
target = array;
pointer = target.GetLowerBound(0) - 1;
}
}
class Sample : IEnumerable //IEnumerableを継承してGetEnumeratorの返値にIEnumeratorインターフェイスを実装したクラスと扱う値を渡す
{
Vector2[] ar = {
new Vector2(12,24),
new Vector2(54,32),
new Vector2(88,99),
};
IEnumerator IEnumerable.GetEnumerator()
{
return new MyEnumerator(ar);
}
}
<実装側>
Sample s = new Sample();
foreach (Vector2 v in s)
{
Console.WriteLine("x={0} y={1}", v.X, v.Y);
}
イベント処理
namespace ConsoleApplication1
{
public delegate void SampleEventHandler(object sender, string str); //デレゲート型(データ型)の定義
class Class1
{
public event SampleEventHandler sampleEvent; //イベントを定義 SampleEventHandlerデレゲート型の変数sampleEvent
public void handler(object o, string str) //イベントで呼び出される予定の関数
{
Console.WriteLine(str);
}
static void Main(string[] args)
//[[XNA]]でイベントを利用する場合はここを普通の関数に変更する
//基本的にイベント呼び出しはイベントが定義されたクラス内からしか行うことができない制限がある
{
Class1 target = new Class1();
target.sampleEvent += new SampleEventHandler(target.handler);
//イベントにハンドラを追加(+=演算子を使うとハンドラの追加、-=は削除)
//newしているのはデレゲート型にあてはめた関数のインスタンスを渡す為
target.sampleEvent(target, "イベント実行時に値を渡しています");
//イベントの実行(イベント変数を関数のように使って実行する)
//第一引数はそのイベントを発生させたオブジェクト
//第二引数はハンドラに渡す引数
}
}
}
継承と多態性
シンプルな継承
<定義側>
public class A
{
public void Foo()
{
Console.WriteLine("Aのメソッド");
}
}
public class B : A
{
public new void Foo() //ここのnewは省略しても全然問題ないが利用者のことを考えてつけておくとよい。
{
Console.WriteLine("Bのメソッド");
}
}
<呼び出し側>
A a = new A();
B b = new B();
a = b; //アップキャスト(これは安全)この反対の代入はダウンキャストとなりNG
a.Foo();
b.Foo();
((A)b).Foo(); //ちなみにこうするとbから基底クラスAのFooが呼べる
<出力>
Aのメソッド (アップキャストしてもAを憶えている)
Bのメソッド
Aのメソッド
オーバーライドを使用した場合
<定義側>
public class A
{
public virtual void Foo()
{
Console.WriteLine("Aのメソッド");
}
}
public class B : A
{
public override void Foo()
{
Console.WriteLine("Bのメソッド");
}
}
public class C : A
{
public override void Foo()
{
Console.WriteLine("Cのメソッド");
}
}
<呼び出し側>
A a = new A();
B b = new B();
a.Foo();
a = b; //アップキャスト
a.Foo();
b.Foo();
((A)a).Foo(); //ちなみにベースクラスにキャストしても出力結果を見れば書き換えられてしまっています
C c = new C();
a = c; //もう一度アップキャスト、今度はC型
a.Foo();
<出力>
Aのメソッド
Bのメソッド //アップキャストによりオーバライドでメソッドまで書き換えられている
Bのメソッド
Bのメソッド //完全に書き換えられてしまっています
Cのメソッド //アップキャストなら何度でもメソッドを書き換えられる
クラス配列(クラスのインデクサ)
作成例
<定義側>
public class Bar
{
private int mValue;
/// <summary>
/// コンストラクタ
/// ここで値をセットして初期化する
/// </summary>
/// <param name="value">メンバー変数への値のセット</param>
public Bar()
{
this.mValue = default(int);
}
public Bar(int value)
{
this.mValue = value;
}
public void BarMethod()
{
++this.mValue;
}
}
<呼び出し側>
//配列じゃなく単体の場合インスタンスと値型の既定コンストラクタのnew呼び出しは同時に行える。
Bar simple = new Bar();
//配列の場合の作り方。インスタンス配列(型の参照配列)を最初に作成し値型の既定コンストラクタのnew呼び出しで中身を詰めていく
Bar[] accessBar = new Bar[5]
{
new Bar(12),
new Bar(24),
new Bar(48),
new Bar(92),
new Bar(104),
};
//こういう作り方もあるが、この場合bar[1]はコンストラクタでnewして添字を作成しているので取り扱いOK
//しかしbar[0]やbar[2]の参照はnullのままなので利用するとエラーになる
Bar[] bar = new Bar[3]; //参照NULLのインスタンス配列が3つできる
bar[1] = new Bar(12);
bar[1].BarMethod();
//bar[2].BarMethod(1024); //ここはエラー(System.NullReferenceException NULLを参照してエラー)
メモ:
C#では「Bar simple;」とだけするとBar型のインスタンスを参照する入れ物としての変数が用意されるだけで、Barクラスのインスタンスが実際に作られるわけではない。
インスタンスを作るには、newキーワードを使い「simple = new Bar();」で空っぽであった変数simpleに、インスタンスへの参照を代入(=)する。
そうすると変数simpleを経由して、生成したインスタンスにアクセスすることがはじめて可能となる。配列に関しても同じ考えを持つ必要がある。
最終更新:2012年03月20日 21:32