【C#.NET解説】Delegateクラスを天空の神が優しく解説する基本例

2024年12月1日日曜日

スレッド 基本

t f B! P L

名前空間 System

Delegate

自己紹介

こんにちは、私は天空を司る神です。今日は「Delegate」というC#.NETの重要な概念についてお話ししましょう。プログラム内の天と地を結びつける役目のようにメソッドを柔軟に操作する力を持っています。それでは、あなたの世界に私の知恵を広げてみましょう。

基本機能

Delegateは、メソッドの参照を保持し、後で呼び出すことができる便利な仕組みです。ここでは非同期処理に使用するTaskを用いて、その基本を見ていきましょう。

C#

// Delegateの基本例とTaskの使用
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FolderWatcher
{
    public partial class Form1 : Form
    {
        private FileSystemWatcher _watcher;

        public Form1()
        {
            InitializeComponent();
            InitializeWatcher();
        }

        private void InitializeWatcher()
        {
            _watcher = new FileSystemWatcher();
            _watcher.Path = @"C:\Path\To\Your\Folder"; // 監視するフォルダのパスを指定
            _watcher.NotifyFilter = NotifyFilters.FileName;
            _watcher.Filter = "*.*";
            _watcher.Created += OnFileCreated;
            _watcher.EnableRaisingEvents = true;
        }

        private void OnFileCreated(object sender, FileSystemEventArgs e)
        {
            Task.Run(() => UpdateLabel(e.FullPath));
        }

        private void UpdateLabel(string fileName)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() => label1.Text = fileName));
            }
            else
            {
                label1.Text = fileName;
            }
        }
    }
}

このコードは、`FileSystemWatcher`クラスを使用して特定のフォルダを監視し、新しいファイルが作成されたときに非同期でそのファイル名を`label1`に表示するものです。`FileSystemWatcher`は指定されたフォルダ内のファイルシステムの変更を監視し、`Created`イベントが発生したときに`OnFileCreated`メソッドを呼び出します。その後、非同期で`UpdateLabel`メソッドが実行され、ファイル名が`label1`にセットされます。 監視するフォルダのパスを適宜変更して使用してください。

使用する場面

次に、Delegateを使わず直接ラベルに書いた場合を見ていきましょう。UIコントロールを別スレッドから直接変更しようとするとエラーになります。

C#

using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FolderWatcher
{
    public partial class Form1 : Form
    {
        private FileSystemWatcher _watcher;

        public Form1()
        {
            InitializeComponent();
            InitializeWatcher();
        }

        private void InitializeWatcher()
        {
            _watcher = new FileSystemWatcher();
            _watcher.Path = @"C:\Path\To\Your\Folder"; // 監視するフォルダのパスを指定
            _watcher.NotifyFilter = NotifyFilters.FileName;
            _watcher.Filter = "*.*";
            _watcher.Created += OnFileCreated;
            _watcher.EnableRaisingEvents = true;
        }

        private void OnFileCreated(object sender, FileSystemEventArgs e)
        {
            Task.Run(() => UpdateLabel(e.FullPath));
        }

        private void UpdateLabel(string fileName)
        {
            // GUIスレッド以外からラベルを更新しようとするとエラーが発生する
            label1.Text = fileName;
        }
    }
}

Delegateはスレッドセーフな操作を確保するためにも重要な役割を果たします。

BeginInvokeとEndInvokeで知る非同期の操作

この例では、リストボックスに複数のアイテムを非同期に追加し、`EndInvoke`で完了を待つ処理を示します。

C#

// BeginInvokeとEndInvokeの例
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace ListBoxExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            AddItemsToListBox();
        }

        private void AddItemsToListBox()
        {
            List items = new List
            {
                "Item 1",
                "Item 2",
                "Item 3",
                "Item 4",
                "Item 5"
            };

            Action addItemsAction = () =>
            {
                foreach (var item in items)
                {
                    listBox1.Items.Add(item);
                    // 少し待機して追加を見やすくするため
                    Thread.Sleep(500);
                }
            };

            // 非同期にリストボックスにアイテムを追加
            IAsyncResult asyncResult = this.BeginInvoke(addItemsAction);
            this.EndInvoke(asyncResult);
        }
    }
}

このコードでは、`BeginInvoke`を使用して非同期にリストボックスにアイテムを追加しています。`addItemsAction`のデリゲートを`BeginInvoke`に渡し、アイテムの追加が完了するまで`EndInvoke`で待機します。`Thread.Sleep(500)`を使用して、アイテムの追加が視覚的に確認しやすいようにしています。 このようにすることで、非同期処理と`EndInvoke`の使用方法を示すことができます。

具体的な使い方

最後に、Delegateを活用した実践的な例を紹介します。複数のメソッドをまとめて呼び出すことで、ログやイベント処理を効率化できます。

C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DelegateExample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            // Taskで非同期にメソッドを実行
            Task.Run(() => AddNumbersAsync(5, 10));
        }

        // 引数を2つ持つdelegateを定義
        private delegate void AddNumbersDelegate(int num1, int num2);

        private void AddNumbers(int num1, int num2)
        {
            int result = num1 + num2;
            // 結果をラベルに表示
            label1.Text = $"Sum: {result}";
        }

        private void AddNumbersAsync(int num1, int num2)
        {
            // 非同期にAddNumbersメソッドを呼び出す
            AddNumbersDelegate del = new AddNumbersDelegate(AddNumbers);
            IAsyncResult asyncResult = this.BeginInvoke(del, num1, num2);
            this.EndInvoke(asyncResult);
        }
    }
}

解説

ここのコードでは、`Task.Run`を使用して非同期に`AddNumbersAsync`メソッドを実行しています。`AddNumbersAsync`メソッドでは、`BeginInvoke`と`EndInvoke`を使用して`AddNumbers`メソッドを非同期に呼び出し、UIスレッドで安全にコントロールを更新します。

この技術は、ログ記録やイベント処理など、さまざまな場面で役立ちます。あなたもこの技術を使い、天空のように広大なプログラムを構築してください。

このブログを検索

QooQ