【C#】delegate

実行環境

■Windows10
■Visual Studio Community 2019
■WPF(Prism Blank App)

 

delegateとは?

delegateとは、メソッドを参照するための型、もしくは関数を代入できる変数です。

実際にコードを書いていきましょう。

【補足】delegateは.NET Framework(1.0)からの機能

deleagateは.NET Frameworkのバージョン1.0からある機能です。
そして、バージョン2.0から匿名メソッドとPredicate、ジェネリックが使えるようになりました。
ラムダ式とLINQはバージョン3.0から使えるようになった機能です。

 

delegateの実装

まずは、delegateを使っていないサンプルのコードを書いていき、その後にdelegateを使っての実装を行っていきたいと思います。

 

delegateなしでの実装

プロジェクトを作成。
xamlにButtonを1つ用意します。

<Window x:Class="wpf_ramudashiki.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="350" Width="525">
    <Grid>
        <StackPanel HorizontalAlignment="Left" Margin="10">
            <Button Content="Button1"
                Command="{Binding Button1Command}"
                Height="30"
                Width="50">
            </Button>
        </StackPanel>        
     <ContentControl prism:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Window>

F5キーもしくは開始ボタンで実行。

上記のようになっていれば、OKです。

続いてViewModelにButton1を押下した時の処理を書きます。

private void Button1CommandExecute()
{
  /* string配列を用意 */
  var values = new string[] { "あ", "いい", "ううう", "ええええ", "おおおおお" };

  /* 結果を入れる箱を用意 */
  var result = new List<string>();

  /* valuesを回してvalに入れる */
  foreach (var val in values)
  {
    if (val.length >= 4)
    {
       result.Add(val);
    }
  }
  Console.WriteLine(string.Join(",", result));
}

もう一度実行。

右下のコンソール上に「ええええ, おおおおお」が表示されているかと思います。
これは、foreach文の中にあるif文で処理した結果です。
string配列の中で要素数が4つ以上のものをコンソール上に表示させています。

delegateなしでの実装(共通関数)

次に、上記の実装を共通関数を使って実装していきます。
共通関数とは、同じ処理を一つの関数としてまとめておくことです。
そうすることで、毎回毎回同じ処理を書かなくて済みます。

先程のButton1CommandExecuteメソッドの下に下記のメソッドを追加します。

        private string[] GetValue1(string[] values)
        {
            var result = new List();
            foreach (var val in values)
            {
                if (val.Length >= 4)
                {
                    result.Add(val);
                }
            }
            return result.ToArray();
        }

このGetValue1をButton1CommandExecuteメソッド内で下記のように使用します。

        private void Button1CommandExecute()
        {
            var values = new string[] { "あ", "いい", "ううう", "ええええ", "おおおおお" };
            var result = new List();
            foreach (var val in values)
            {
                if (val.Length >= 4)
                {
                    result.Add(val);
                }
            }
            Console.WriteLine(string.Join(",", result));
            /* ここでGetValue1メソッドを使う */
            var result2 = GetValue1(values);
            Console.WriteLine(string.Join(",", result2));
        }

再度実行。

コンソール上に2つ同じ表示がされていればOKです。
これで、共通関数完了です。

delegateなしでの実装(引数が複数)

加えて、引数が複数ある場合の実装も行います。

      <Grid>
        <StackPanel HorizontalAlignment="Left" Margin="10">
            <Button Content="Button1"
                Command="{Binding Button1Command}"
                Height="30"
                Width="50">
            </Button>            

       /* Button2を追加 */
            <Button Content="Button2"
                Command="{Binding Button2Command}"
                Height="30"
                Width="50">
            </Button>
        </StackPanel>        
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />    
      </Grid>

xamlにButton2を追加します。
次に、ViewModel側に処理を書いていきます。

        public MainWindowViewModel()
        {
            Button1Command = new DelegateCommand(Button1CommandExecute);
            /* コンストラクタ内 */
            Button2Command = new DelegateCommand(Button2CommandExecute);
        }

        /* Button1の処理は省略 */

        /* Commandを作成 */
        public DelegateCommand Button2Command { get; }        

        /* Button2を押下した際の処理 */
        private void Button2CommandExecute()
        {
            var values = new string[] { "あ", "いい", "ううう", "ええええ", "おおおおお" };
       /* 引数を2つにしている(3はGetValue2メソッドのlengに入る)*/
            var result = GetValue2(values, 3);
            Console.WriteLine(string.Join(",", result));
        }

        /* GetValue2メソッドを作成 */
        private string[] GetValue2(string[] values, int leng)
        {
            var result = new List<string>();
            foreach (var val in values)
            {
                if (val.Length >= leng)
                {
                    result.Add(val);
                }
            }            return result.ToArray();
        }

実行。

Button2を押下すると、コンソール上にstring配列内の3文字以上の文字が返ってきます。
これがdelegateなしでの実装になります。

ただ毎回、下記のように変数(leng)を(ここでは3)指定するのは面倒です。

if (val.Length >= leng)

また、汎用性も低くなります。
そこで、delegateが誕生しました。
このdelegateを使うと、今まで変数で扱っていた箇所に(ここではleng)式(メソッド)を使うことができるようになります。

コードを書いてみていきましょう。

delegateを使っての実装(引数が1つ)

xaml側にButtonを1つ追加します。

      <Grid>
        <StackPanel HorizontalAlignment="Left" Margin="10">
            <Button Content="Button1"
                Command="{Binding Button1Command}"
                Height="30"
                Width="50">
            </Button>           

            <Button Content="Button2"
                Command="{Binding Button2Command}"
                Height="30"
                Width="50">
            </Button>         

       /* Button3を追加 */
            <Button Content="Button3"
                Command="{Binding Button3Command}"
                Height="30"
                Width="50">
            </Button>
        </StackPanel>        
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />    
      </Grid>

続いてViewModel側にもButton2と同様の処理を記載。

       public MainWindowViewModel()
        {
            Button1Command = new DelegateCommand(Button1CommandExecute);
            Button2Command = new DelegateCommand(Button2CommandExecute);
            /* Button3を追加 */
            Button3Command = new DelegateCommand(Button3CommandExecute);
        }

        /* Button1と2の処理は省略 */

        /* Commandを作成 */
        public DelegateCommand Button3Command { get; }        

        /* Button3を押下した際の処理 */
        private void Button3CommandExecute()
        {
            var values = new string[] { "あ", "いい", "ううう", "ええええ", "おおおおお" };
            var result = GetValue3(values, 3);
            Console.WriteLine(string.Join(",", result));
        }

ここから、MainWindowViewModel.csにdelegateを書いていきます。

/* 戻り値 メソッド名 引数を書く(戻り値はbool以外でも可) */
  delegate bool LengCheck(string value);

上記のようにしてdelegateは書きます。
続いて、delegateを使うためのメソッドを書きます。

  /* 引数にdelegateのLengCheckを入れる */
  private string[] GetValue3(string[] values, LengCheck lengCheck)
  {
     var result = new List<string>();
      foreach (var val in values)
      {
      /* ここにLengCheckの変数lengcheckを入れる */
        if (lengCheck(val))
         {
           result.Add(val);
         }
      }            
     return result.ToArray();
  }

  /* LengCheckに入れるためのメソッドを用意 */
  /* ここは、delegateで指定した戻り値と引数を合わせる必要がある */
  private bool Method1(string value)
  {
    /* 要素数が4のstring配列を返す処理 */
    return value.Length == 4;
  }

これで、delegateを使う準備が出来ました。
最後に、Button3CommandExecuteメソッドにMethod1を入れていきます。

/* Button3を押下した際の処理 */
private void Button3CommandExecute()
{
  var values = new string[] { "あ", "いい", "ううう", "ええええ", "おおおおお" };
   /* GetValue3の引数を3からMethod1に変更 */
  var result = GetValue3(values, Method1);
  Console.WriteLine(string.Join(",", result));
}

実行。

Button3を押下すると、要素数が4の「ええええ」がコンソール上に表示されるかと思います。
このようにすると、if文の中で要素数を直接指定していた箇所でメソッドが使えるようになります。
最後に引数が2つの場合の実装も行っておきます。

delegateを使っての実装(引数が2つ)

同様にButton4をxaml側に追加。
新たにButton4を押下した時の処理とdelegateを下記のように記載します。

public DelegateCommand Button4Command { get; }

private void Button4CommandExecute()
{
  var values = new string[] { "あ", "いい", "ううう", "ええええ", "おおおおお" };
  /* この場合は、要素数が2つ以上のものを取得する処理になっています */
  var result = GetValue4(values, 2, Method6);
  /* ちなみに、上記はvar resulet = GetValue4(values, 4, Method5); などに置き換えできます */
  Console.WriteLine(string.Join(",", result));
}

delegate bool LengCheck4(string value, int leng);

private string[] GetValue4(string[] values, int leng, LengCheck4 lengCheck)
{
  var result = new List<string>();
   foreach (var val in values)
   {
      if (lengCheck(val, leng))
       {
         result.Add(val);
       }
    }
    return result.ToArray();
        
}        

private bool Method5(string value, int leng)        
{
  return value.Length == leng;
}        

private bool Method6(string value, int leng)
{
  return value.Length >= leng;
}

実行。

Button4を押下すると、要素数が2以上の文字がコンソール上に表示されます。
また、要素数とメソッドを変更すれば、表示される文字も変えることが可能となります。
以上が基本的なdelegateの使い方になります。

しかし、この方法だと毎回、メソッドを書いていかなければいけません。
匿名メソッドを使用すれば、もう少し効率の良いコードが書けます。

【C#】匿名メソッド・Predicate

2021.02.16