//tips
//C#理解
イベントはほぼデリゲートと言えるが、いくつか制約があり、
・イベントハンドラーを呼び出せるのは、イベントをメンバーとするクラスの内部から
・外部からイベントに対して行える操作はイベントハンドラーの追加と削除のみ
public static void Main()
{
TestClass t = new TestClass();
t.ThreeEvent +=delegate{ Console.WriteLine(“XX”);};
//t.ThreeEvent =delegate{ Console.WriteLine(“XX”);};代入は不可
t.OnThreeEvent();
//イベントを直接呼び出せない。t.ThreeEvent();は不可
}
イベントでは、イベントハンドラーの登録・解除だけをクラスの外部に公開して、その他は非公開にできる。
逆にデリゲートでは、全ての操作を外部に公開するか、非公開にするしかできないという極端な性質を持つ。
ここからは非同期処理も踏まえてみていく。
非同期処理は複数の処理が並列・並行に処理されるもので、時間がかかる処理は処理を継続させ、その間に別の処理も同時に行わせることができる。
これは複数コアのCPUが当たり前になって、そのパフォーマンスを活かすために必須の技術となっている。
非同期処理の際にスレッドという概念が用いられ、これはプログラムの最小実行単位を指す。
OS上でプログラムを起動すると、プロセスと呼ばれる仮想領域が作られ、そこにCPUやメモリが割り当てられることになる。これにより他のプロセスとは独立して動作する。
スレッドはこのプロセス内でプログラムを実行しているもので、プロセスはスレッド単位で構成され、必ず一つのスレッドが含まれることになる。
現在ではスレッドの代わりにタスクという表現が用いられており、Taskクラスとして基本的には使用される。
class MainClass
{
static public void ThreadMethod()
{
Thread.sleep(3000);
Console.Writeline(“finish”)
}
public static void Main()
{
Console.WriteLine(“start”);
var task=Task.Run(new Action(ThreadMethod));
//RunメソッドにActionデリゲートを渡す
Task.Wait();
//タスクが完了するまで待機
}
}
TaskクラスのRunメソッドでタスクの作成から実行まで行える。引数にはタスクで行うデリゲートを指定。Task内部ではThreadPoolクラスを使用している。
処理を並行して実行できるparallelクラスを使用すると、二つの処理を並列に実行できる。下記では、メソッドによる3秒待機の処理とメインメソッドに描かれた5秒待機の処理があり、2つの処理はスレッド化されて、同時進行で実行される。
class MainClass
{
static public void ThreadMethod()
{
Thread.Sleep(3000);
Console.WriteLine(“finish”);
}
public static void Main()
{
Console.WriteLine(“start”);
//InvokeメソッドにActionデリゲートを渡す
Parallel.Invoke(new Action(ThreadMethod),
()=>
{
Thread.Sleep(5000);
Console.WriteLine(“finish”);
});
}
}
Parallel.Invokeメソッドの引数には複数の処理をコンマで区切って指定可能。
async修飾子とawait演算子についても確認する。これらは同期処理のような書き方で非同期処理を記述できる便利なもの。
asyncは非同期に実行したいメソッドの戻り値の型の前に記述し、非同期メソッドであることを明示する。
メソッド内にawaitが含まれるときは必ず指定する。
async Task メソッド名()
{
…
await タスクオブジェクト;
…
}
戻り値を必要としない場合はTask型、必要な場合はTask<T>型を使用する。
await は指定したタスクオブジェクトが完了するまで実行を待機させるもの。
Class MainClass
{
public static async Task ThreadMethodAsync()
{
await Task.Run(()=>{
Thread.Sleep(3000);
Console.WriteLine(“finish”);
});
}
public static void Main()
{
Console.writeLine(“start”);
Task t=ThreadMethodAsync();
t.Wait();
}
}
ThreadMethodAsyncメソッドをasyncをつけて非同期メソッドとして定義している。
戻り値を返す場合は、
Class MainClass
{
public static async Task<long> ThreadMethodAsync()
{
long val=0;
return await Task.Run(()=>{
for(long I=1;,I<=10000000;i++)
{
val +=i;
}
return val;
});
}
public static void Main()
{
Console.writeLine(“start”);
Task<long> t=ThreadMethodAsync();
t.Wait();
//タスクが完了するまで終了しないように待機
Console.WriteLine(t.Result);
}
}
タスクの結果を参照するにはTask型のResultプロパティを使用する。
今までは.NETのクラスライブラリなどで定義された名前空間のみを使用してきたが、先のvivoxの拡張のように、多数のソースファイルなどがあるようなプロジェクトでは、自身で作成する必要も出てくる。
簡単な名前空間の構造を作ってみる。window名前空間の中に、titleクラスとdrawing名前空間を作成。
namespace Window
{
class Title
{
//titleの内容
}
Namespace Drawing
{
class Image
{
//imageの内容
}
}
}
この名前空間の内容をusingディレクティブを使用することで、名前空間の指定を省略して、コードを記述できる。
先に述べた拡張メソッドにより、クラスの外部から機能を拡張でき、独立したstaticクラスの静的メソッドとして定義する。
パラメータではthisで拡張するクラスを指定。
public static 拡張メソッド名(this 拡張すべき型,パラメータリスト)
{
//拡張メソッドの定義
}
Public static void checkJ(this TestClass t)などとするとTestClassに初めから存在しているメソッドcのようにcheckJを扱える。