【C#】デリゲートについて

概要

  • デリゲートに関してまとめる。
    • 何回目だろうか。
    • きっとそれだけ分かりにくい内容なのかもしれない。
      • 実際に手を動かさなければ覚えられない。

delegate

概要

  • delegateとは、メソッドを参照するための型(変数)である!
    • 端的に言えば、変数valueにメソッドsomeMethodを格納する。

文法(C#)

  • 変数宣言
delegate 戻り値の型 デリゲート型名(引数リスト);
  • インスタンス化
デリゲート型名 変数名 = new デリゲート型名(メソッド名);

サンプル

最も基本的なデリゲート

using System;

class Sample1{
    // delegate変数の宣言
    private delegate MyDelegate(string arg1);

    /*
     * メインメソッド
     */
    public static void Main(){
        // 処理を変数に格納
        MyDelegate _myDelegate = new MyDelegate(SomeMethod);

        // 格納した処理を使用
        _myDelegate("デリゲートテスト");
    }

    /*
     * 引数1の値を出力する
     */
    public void SomeMethod(string msg){ Console.WriteLine(msg); }
}
  • 出力結果
デリゲートテスト

暗黙の型変換

  • 下記で暗黙の型変換が可能
デリゲート型名 変数名 = メソッド名;
using System;

class Sample2{
    // delegate変数の宣言
    private delegate void MyDelegate(string arg1);

    /*
     * メインメソッド
     */
    public static void Main(){
        // 処理を変数に格納
        MyDelegate _myDelegate =SomeMethod;

        // 格納した処理を使用
        _myDelegate("暗黙の型変換によるデリゲートテスト");
    }

    /*
     * 引数1の値を出力する
     */
    public static void SomeMethod(string msg){ Console.WriteLine(msg); }
}
  • 実行結果
暗黙の型変換によるデリゲートテスト

インスタンスメソッドの代入

  • デリゲートにはクラスメソッドとインスタンスメソッド
    • クラスメソッド(staticメソッド)
    • インスタンスメソッド(object.メソッド名()で呼ばれるメソッド)
using System;

/*
 * メインクラス
 */
class Sample3{
    private delegate void MyDelegate();

    public static void Main(){
        Person _person = new Person("John");
        MyDelegate _myDelegate = new MyDelegate(_person.ShowName);
        _myDelegate();
    }
}

/*
 * Personクラス
 */
public class Person{
    private string name;

    // コンストラクタ
    public Person(string name) {
        this.name = name;
    }

    // サンプルメソッド
    public void ShowName(){
        Console.WriteLine("My Name is {0}",this.name);
    }
}
  • 出力結果
My Name is John

マルチキャストデリゲート(複数メソッドの代入)

using System;

/*
 * メインクラス
 */
class Sample3{
    private delegate void MyDelegate();

    public static void Main(){
        Person _person = new Person("John");
        Person _person2 = new Person("Bob");

        MyDelegate _myDelegate = new MyDelegate(_person.ShowName);
        _myDelegate += new MyDelegate(_person2.ShowName);
        _myDelegate += new MyDelegate(Echo);
        _myDelegate();
    }

    public static void Echo(){
        Console.WriteLine("Echo Method!");
    }
}

/*
 * Personクラス
 */
public class Person{
    private string name;

    // コンストラクタ
    public Person(string name) {
        this.name = name;
    }

    // サンプルメソッド
    public void ShowName(){
        Console.WriteLine("My Name is {0}",this.name);
    }
}
  • 実行結果
My Name is John
My Name is Bob
Echo Method!

非同期処理

  • ThreadPoolを使用することで、非同期呼び出しを使用したマルチスレッド処理を行うことが可能。
文法
// 非同期処理の開始
IAsyncResult BeginInvoke(引数リスト…, AsyncCallback callback, object state);
int EndInvoke(引数リスト…, IAsyncResult ar);
using System;
using System.Threading;

class Sample5{
    private delegate void MyDelegate();
    static void Main(){
        MyDelegate _myDelegate = SomeMethod;
        // 非同期処理の開始
        IAsyncResult ar = _myDelegate.BeginInvoke(null,null);

        for (int i=0; i<5; i++){
            Thread.Sleep(100);
            Console.WriteLine("Main ({0})",i);
        }

        // 非同期処理の終了を待つ。
        _myDelegate.EndInvoke(ar);

        Console.WriteLine("End");
    }

    private static void SomeMethod(){
        for (int i=0;i<5;i++){
            Thread.Sleep(100);
            Console.WriteLine("SomeMethod1 ({0})",i);
        }
    }
}
  • 実行結果
SomeMethod1 (0)
Main (0)
SomeMethod1 (1)
Main (1)
Main (2)
SomeMethod1 (2)
SomeMethod1 (3)
Main (3)
SomeMethod1 (4)
Main (4)
End

参考サイト

【C#】マルチスレッド処理に関して

03_マルチスレッド処理に関して

概要

  • マルチスレッド処理に関するまとめを行う。

マルチスレッド処理

  • マルチプロセスとは別物なので注意!
    • プロセス -> exeファイル
      • スレッド -> プロセス内の処理

並列稼働と並行稼働

  • 並列稼働と並行稼働は違うらしい。
    • 並列稼働: 複数のCPUで同時に処理を行う。
    • 並行稼働: 一つのCPUが複数の処理を切り替えながら動作し、疑似的に複数のCPUがあるように処理を行う。
マルチスレッドの動作原理
マルチスレッドの動作原理

マルチスレッド処理の目的

1. レスポンスタイムの向上

  • 重い処理が行われている間に、他の処理を行わせることで、レスポンスタイムが向上する。
  • 複数のクライアントに対して同時に処理を行うことができる。

2. スループット(単位時間当たりの処理性能)の向上

  • ディスク上にあるすべてのファイルをリストアップするような処理を行うとき、
    ディスクにアクセスしている時間は非常に長くなるが、その間CPUでの処理はほとんどできない。

    • このようなCPUの空き時間がパフォーマンス向上の余地になる。

マルチスレッド処理の実現方法

 
方法 メリット デメリット
スレッド(Thread) ・ 各スレッドに優先順位を設定できる

・ スレッドの一時停止/再開/中断を行うことができる

・ スレッドの作成と破棄を繰り返すとパフォーマンスが落ちる

・ メソッドにパラメータを設定できない

・ メソッドの戻り値を得るのが困難

スレッドプール(ThreadPool) ・ 効率よく複数のスレッドを実行できる

・ object型のパラメータを1つだけ設定できる

・ パラメータがobject型1つのみ

・ メソッドの戻り値を得るのが困難

・ 優先順位付けや待機、停止など、スレッドの細かな制御が難しい

・同時に実行できるスレッドの数が制限されている

デリゲート(BeginInvoke) ・ メソッドに型のあるパラメータを指定できる

・ 簡単に戻り値を得ることができる

・ 優先順位付けや待機、停止など、スレッドの細かな制御が難しい

・ 同時に実行できるスレッドの数が制限されている

タイマー(Timer) ・一定時間間隔でメソッドを実行することができる ・スレッドプールがいっぱいだとうまく動作しない

スレッド

  • Threadクラスで指定したメソッドを別スレッドで起動(実行)する仕組みを提供する。
  • スレッドの生成にはそれなりのリソースが消費されえるため大量にスレッドが処理される場合、パフォーマンスが低下する。

サンプルコード

using System;
using System.Threading;

public class List1_1
{
    public static void Main()
    {
        Thread threadA = new Thread(
                new ThreadStart(ThreadMethod));// (1)

        threadA.Start();// (2)

        for(int i = 0; i < 100; i++)
        {
            Thread.Sleep(5);
            Console.Write("B");
        }
    }

    //別スレッドで動作させるメソッド
    private static void ThreadMethod()
    {
        for(int i = 0; i < 100; i++)
        {
            Thread.Sleep(5);
            Console.Write("A");
        }
    }
}
  • 実行結果
ABBABAABBABAABBAABBABAABABBABAABABBAABABABABABABBABABABABABABABAABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
解説
  • (1): Threadクラスのインスタンスを生成する。
    • ThreadStartクラスのコンストラクタに別スレッドで動作させたいメソッド名を指定する。
        Thread threadA = new Thread(
                new ThreadStart(ThreadMethod));// (1)
  • (2): 別スレッドによる処理を実行する。
    • ThreadオブジェクトのStartメソッドを呼び出す。

スレッドプール

  • ThreadPoolクラスで提供される。
  • キューに入れられたリクエスト(処理)をスレッドプールが用意しているスレッドにより次々に実行していく仕組み。
  • 一度確保したスレッドのリソースをできる限り再利用するように設計されている。
    • スレッドで問題になっていたパフォーマンス低下が解消される。

サンプルコード

using System;
using System.Threading;

public class List2 {
    public static void Main() {
        // ThreadMethodをスレッドプールで実行できるように
        // WaitCallbackデリゲートを作成
        WaitCallback waitCallback = new WaitCallback(ThreadMethod);// (1)

        // スレッドプールに登録
        ThreadPool.QueueUserWorkItem(waitCallback,"A");// (2)
        ThreadPool.QueueUserWorkItem(waitCallback,"B");// (3)

        Console.ReadLine();
    }

    private static void ThreadMethod(object state) {
        for(int i = 0; i < 100; i++){
            Thread.Sleep(5);
            Console.Write("{0}",state);

        }
    }
}
  • 実行結果
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
解説
  • (1): スレッドプールで使用できるようにデリゲートを作成。
    • 処理を行いたいメソッドを引数としてWaitCallbackオブジェクトを作成する。
WaitCallback waitCallback = new WaitaCallback(ThreadMethod);
  • (2): スレッドを開始する。
    • ThreadPool.QueueUserWorkItemメソッドを使用する。
      • 引数は、第一引数にデリゲート、第二引数にobject型のパラメータを指定すること。

デリゲート

  • デリゲートに関するまとめはこちら
  • デリゲート=メソッドの呼び出しをラッピングしたもの
  • デリゲートを通じて間接的に呼び出すことができる。
  • デリゲートに登録されたメソッドを実行するには、普通に実行するInvokeメソッドのほかに、
    別スレッドでメソッドを実行するBeginInvokeメソッドが使用できる。

    • デリゲートは上述したスレッドプールを利用して、登録されたメソッドを実行するという仕組みになっている。

サンプル

using System;
using System.Threading;

public class List3
{
    // 戻り値とパラメータのあるデリゲート
    delegate DateTime ThreadMethodDelegate(string c);// (1)
    static ThreadMethodDelegate threadMethodDelegate;// (2)

    public static void Main(){
        threadMethodDelegate = new ThreadMethodDelegate(ThreadMethod); // (3)

        // デリゲートによるスレッド処理呼び出し
        threadMethodDelegate.BeginInvoke(".",new AsyncCallback(MyCallback), DateTime.Now); // (4)

        Console.ReadLine();
    }

    private static DateTime ThreadMethod(string c){
        // 10ミリ秒ごとに100回cを出力
        for (int i=0; i<100;i++){
            Thread.Sleep(10);
            Console.Write(c);
        }
        return DateTime.Now;
    }

    // スレッド処理終了後に呼び出されるコールバック・メソッド
    private static void MyCallback(IAsyncResult ar)// (5)
    {
        DateTime result = threadMethodDelegate.EndInvoke(ar);// (6)
        DateTime beginTime = (DateTime)ar.AsyncState; // (7)

        Console.WriteLine();
        Console.WriteLine("{0}に処理を開始し、{1}に処理を完了しました。", beginTime,result);
    }
}
解説
  • ThreadMethodという名前のメソッドを、デリゲートを使用して別スレッドに実行し
    ThreadMethodメソッドの処理が終了した時点でMyCallbackメソッドが自動的にコールバックされるように設定している。
  • (1)
    • 別スレッドとして処理したメソッドをデリゲート宣言する。
      • delegate DateTime ThreadMethodDelegate(string c);// (1)
      • このときのパラメータの型と個数、戻り値の型は、別スレッドとして処理したメソッドに合わせておく。
        • ThreadMethod自体は、何も特別な部分のない通常のメソッドである。
  • (2),(3)
    • ここで宣言したデリゲートを実際に利用するために、
      デリゲートのインスタンスを作成しフィールド変数threadMethodDelegateとして保持する。

      • static ThreadMethodDelegate threadMethodDelegate;// (2)
      • threadMethodDelegate = new ThreadMethodDelegate(ThreadMethod); // (3)
  • (5)
    • コールバック・メソッドを作成する。
      • コールバック・メソッド: 別スレッドによる処理が終了したことをトリガーとして、自動的に呼び出されるメソッドのことである。
    private static void MyCallback(IAsyncResult ar)// (5)
    {
    }
  • (4)
    • デリゲートのBeginInvokeメソッドを呼び出すことによって、別スレッドの処理を開始する。
      • BeginInvokeメソッドの引数は下記の通り
        • デリゲートを通じて呼び出すメソッドへのパラメータ(複数ある場合は複数指定する。)
        • AsyncCallbackデリゲートでラッピングしたコールバック・メソッド
          • ステート
        • AsyncCallbackデリゲートは、コールバックしたいメソッドをコンストラクタのパラメータに指定して、そのインスタンスを作成する。
        • 最後の引数であるステートjは、object型のオブジェクトを自由に指定できる。
        • 指定したオブジェクトはコールバック・メソッドのパラメータやBeginInvokeメソッドの戻り値であるIAsyncResultオブジェクト内に保持されている。
      • 詳細は「Control.BeginInvoke Method (System.Windows.Forms)」
        // デリゲートによるスレッド処理呼び出し
        threadMethodDelegate.BeginInvoke(".",new AsyncCallback(MyCallback), DateTime.Now); // (4)

タイマー

  • Timerクラスを利用して実現する。

サンプル

using System;
using System.Threading;

public class List4 {
    private static int count = 0;
    public static void Main(){
        // 一定間隔ごとに呼び出すメソッドをTimerCallbackとして登録
        TimerCallback timerCallback = new TimerCallback(ThreadMethod);// (1)

        Console.WriteLine("くるくるまわるよ!");
        // 1秒待ってから、0.1秒ごとにtimerCallbackメソッドを呼び出す
        Timer timer = new Timer(timerCallback, null, 1*1000, 1*100);// (2)

        Console.ReadLine();
    }

    private static void ThreadMethod(object state){ // (3)
        count++;
        string output = "";
        switch(count%7){
            case 0:
                output = "  ( ・ω)";
                count = 0;
                    break;
            case 1:
                output = "  ( ・ )";
                    break;
            case 2:
                output = "  (    )";
                    break;
            case 3:
                output = "  (・  )";
                    break;
            case 4:
                output = "  (・  )";
                    break;
            case 5:
                output = "  (ω・ )";
                    break;
            case 6:
                output = "  (・ω・)";
                break;
        }
        // 行を削除
        Console.Write("\r");
        Console.Write(output);
    }
}
  • 実行結果
List4
List4

参考サイト

連載.NETマルチスレッド・プログラミング入門

ジェネリックとデリゲート

ジェネリクス型

  • クラスのメンバを宣言するときに,

public class SomeClass{

	private strig member1;
	private int member2;

	someClass(){
		this.member1="hoge";
		this.member2=7;
	}
}

としてしまうと,member1member2には,それぞれStringintしか使用できなくなる。

  • ジェネリクスを使用すると,メンバ変数の型をインスタンス時に指定することができる。

public class SomeClass<T1, T2>{

	private T member1;
	private T2 member2;

}

public static void main(String args[]){
	SomeClass<String, int> obj = new SomeClass<String, int>();
}

デリゲートについて

  • 匿名関数
    • メソッドの型

// 単なる型
public delegate void TestMethod(String s);

  • 使用方法は,作成した型で変数を作成し使用する。

public delegate void TestMethod(String s);

TestMethod test = delegate(String msg){
	MessageBox.Show(msg)
};

test("test");
test("hoge");
test("fuga");

  • .NETのフォームのイベントハンドラ等で使用されている。
  • 引数を使用しない匿名関数は,Actionという名前で宣言されている。

Action act = delegate(){
	MessageBox.Show("Action");
}

act()

ラムダ式

  • いちいちdelegateを書くのが面倒くさいので,ラムダ式を使用する。

TestMethod test = delegate(String msg){
	MessageBox.Show(msg)
};

TestMethod test = (nsg) => {
	MessageBox.Show(msg)
};

TODO: C#の部分をJavaに置き換え

以上

C#で鍵を作成=> 暗号化 => 複合

C#で鍵を作成=> 暗号化 => 複合

using System;
using System.Text;
using System.Security.Cryptography;

namespace keygenTest
{
class Program
{
static void Main(string[] args)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
String keyword = Console.ReadLine();
byte[] keywordByte = System.Text.Encoding.UTF8.GetBytes(keyword);
byte[] encryptText = rsa.Encrypt(keywordByte, false);
byte[] decryptText = rsa.Decrypt(encryptText, false);
Console.WriteLine("keyword: " + keyword);
Console.WriteLine("keyword(byte)" + Encoding.UTF8.GetString(keywordByte));
Console.WriteLine("encryptText: " + BitConverter.ToString(encryptText));
Console.WriteLine("decryptText: " + Encoding.UTF8.GetString(decryptText));
Console.ReadKey();
}
}
}

 

出力

hogeeee!!!
keyword: hogeeee!!!
keyword(byte)68-6F-67-65-65-65-65-21-21-21
encryptText: 59-30-83-EC-A0-EE-CA-B3-FE-B5-47-3C-50-4D-17-81-43-09-67-4C-E0-05-0C-07-5A-CC-7F-13-08-A9-E4-89-B0-DB-5F-8B-3D-41-F0-08-DD-6B-4F-9D-00-98-1D-88-48-19-B2-88-45-36-05-B3-75-0E-E9-30-86-D0-37-8D-38-B2-88-8C-20-3F-47-AA-F0-6C-13-9B-34-3E-7E-E6-7C-65-DD-F4-8A-7C-AE-BB-DE-3E-1D-D2-67-DE-F1-E0-37-0C-CD-D1-BF-66-6E-0D-41-E0-FE-77-94-65-67-7C-2D-D0-E8-7A-3C-81-BE-3D-67-50-0B-EE-30-9E-0A-76
decryptText: hogeeee!!!

以上