業務のためのC#・C言語・C++学習

主にC#の文法やWPF周りのアウトプットに利用してます。

【C#】delegateの基礎とActionを用いた応用

delegate「メソッドの引数にメソッドを代入する仕組み」です。そのメリットは複数あるメソッドの共通部分を抜き出し異なる部分だけメソッドとして定義すれば効率良くコードが書けます。今となってはdelegate→匿名メソッド→ラムダ式と進化し、ラムダ式がを良く使いますが、メソッドの非同期処理でdelegateが利用されている場合があるので学習するメリットはあります。

delegateそのものに2つのメソッドを代入する

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate0
{
    delegate void Goal(string str);//宣言
    class Program
    {
        static void Run(string s)
        {
            Console.WriteLine($"現在ゴールまで{s}kmです");
        }
        static void Energy(string s)
        {
            Console.WriteLine($"残りの体力は{s}%です");
        }
        static void Main(string[] args)
        {
            //Goal g = new Goal(Run);//Delegateのインスタンス化
            Goal g = Run;//省略形
            g("10");
            Goal e = Energy;
            e("50");
        }
    }
}

出力結果

delegateの使い方は「delegateの宣言→delegateインスタンス化→delegateインスタンスにメソッド代入」です。
delegateの宣言

delegate 戻り値の型 デリゲート名(引数の型 引数、 ….)
delegate void Goal(string str)でdelegateの宣言。

delegateインスタンス
RunメソッドをdelegateのGoalに代入しつつ、インスタンス化を行う。

Goal g = new Goal(Run);

new演算子の省略形としてメソッドを直接渡してもよい。

Goal g = Run;//省略形

生成したインスタンスに値を代入して出力。
ただし、これだけではdelegateを使わずRunとEnergyメソッドをそのまま利用すればいいのではないかということになりdelegateの良さがまだ見えてきません。

delegateを経由し共通メソッドに2つのメソッドを代入する

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegate1
{
    delegate void DelegateName(string str);

    class Program
    {
        static void WhereToGo(string place)
        {
            Console.WriteLine($"Go to {place}");
        }

        static void HowToGo(string vehicle)
        {
            Console.WriteLine($"Go by {vehicle}");
        }
        void MethodCommon(string[] data, DelegateName output)
        {
            foreach (var value in data)
            {
                output(value);
            }
        }
        static void Main(string[] args)
        {
            var dm = new Program();

            var data = new[] { "Tokyo", "Kyoto" };
            DelegateName delegatename = WhereToGo;
            dm.MethodCommon(data, delegatename);

            var data1 = new[] { "Train", "Car" };
            delegatename = HowToGo;
            dm.MethodCommon(data1, delegatename);
        }
    }
}

出力結果

delegateの使い方を、「delegateの宣言→delegateインスタンス化→delegateインスタンスにメソッド代入」から「delegateの宣言→delegateインスタンス化→delegateインスタンスにメソッド代入→共通メソッドにdelegateインスタンスを代入」にUpdateしています。
共通メソッドMethodCommonにデリゲートDelegateNameを引数としメソッドWhereToGoとHowToGoを代入してます。メソッドが代入されたdelegatenameをdm.MethodCommonの引数に代入すると、「WhereToGoとHowToGoそれぞれのメソッド」と「共通メソッドMethodCommon」の実行内容がそれぞれ実施される。代入するメソッドがさらに増えていけばdelegateを利用する価値があると言えます。

Actionを用いたdelegateの応用その1

先ほど説明したdelegateの宣言→delegateインスタンス化→delegateインスタンスにメソッド代入→共通メソッドにdelegateインスタンスを代入」は少し手間が多くないかい?ということで「delegateの宣言」と「delegateインスタンスにメソッド代入」を削除できるActionを紹介します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace anonymousmethod
{
    //delegate void DelegateName(string str);

    class Program
    {
        static void WhereToGo(string place)
        {
            Console.WriteLine($"Go to {place}");
        }

        static void HowToGo(string vehicle)
        {
            Console.WriteLine($"Go by {vehicle}");
        }
        void MethodCommon(string[] data, Action<string> output)
        {
            foreach (var value in data)
            {
                output(value);
            }
        }
        static void Main(string[] args)
        {
            var dm = new Program();

            var data = new[] { "Tokyo", "Kyoto" };  
            //DelegateName delegatename = WhereToGo;
            dm.MethodCommon(data, WhereToGo);

            var data1 = new[] { "Train", "Car" };
            //delegatename = HowToGo;
            dm.MethodCommon(data1, HowToGo);

        }
    }
}

前回は共通メソッドの引数の型に宣言したdelegateの変数を使っていました。ここでは代わりにActionの型を利用してます。 delegateをわざわざ宣言しなくてもActionを導入することで共通メソッドにメソッドを代入できるようになっています。
Actionはデリゲートとして利用する「値を返さないメソッドの型」だと思ってください。

Actionを用いたdelegateの応用その2

共通メソッドがない場合のAction実用例を示します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Action0
{
    class Program
    {
        static void Main(string[] args)
        {
            Action<string> DelegateName = Method1;
            DelegateName += Mehhod2;

            DelegateName("こんにちは");
        }

        static void Method1(string value)
        {
            Console.WriteLine($"Method1 argument is {value}");
        }

        static void Mehhod2(string value)
        {
            Console.WriteLine($"Method2 argument is {value}");
        }
    }
}

出力結果

ここでのdelegateの使用の流れは「Actionを利用したdelegateインスタンス化→delegateインスタンスにメソッド代入」とたいぶ簡潔しています。メソッドの引数の型が同じで、戻り値がないメソッドが複数ある場合、Actionデリゲートを使えばすっきりとしたコーディングができると言えるでしょう。