LoginSignup
2
0

More than 1 year has passed since last update.

C# ラムダ式でローカル変数を使用するとどうなるのかやってみた

Last updated at Posted at 2023-01-04

はじめに

C# ラムダ式で(ラムダ式の外側の)ローカル変数を使用するとどうなるのか気になり、やってみました。

本記事は実験的な内容であり、この実装方法を推奨するものではありません。実用的な内容でもありません。
(どちらかというと、うっかり書かないように気を付けましょうという内容です。)

実験1

下記のプログラムを実行し、ボタンをクリックするとコンソールに何が表示されるでしょうか?

using System;
using System.Drawing;
using System.Windows.Forms;

class LambdaSample : Form
{
    static readonly int N = 3;
    Button[] btn;

    LambdaSample()
    {
        btn = new Button[N];

        for(int i=0;i<N;i++)
        {
            btn[i] = new Button(){
                Location = new Point(0, 30*i),
                Size = new Size(50, 25),
                Text = i.ToString(),
            };
            Controls.Add(btn[i]);
            btn[i].Click += (s,e)=>{Console.WriteLine(i);}; // ...(※)
        }
    }
    
    [STAThread]
    static void Main(string[] args)
    {
        Application.Run(new LambdaSample());
    }
}

image.png

結果

いずれのボタンを押しても

3

が出力されます。
変数iが、for文を抜けても生き残っているようです。
(※のラムダ式(s,e)=>{...};からiが参照されているのでスコープを抜けても生き残っているよう。)

逆アセンブル結果

ILDASMで見てみると、<>c__DisplayClass3 という謎のクラスが生成され、iがフィールドとして存在しています。

image.png

ちなみに、(※)の部分のコードからiを削除すると下記のようになります。

image.png

実験2

btn[i].Click += (s,e)=>{Console.WriteLine(i);}; // ...(※)

の部分を

btn[i].Click += (s,e)=>{Console.WriteLine(i); i++;}; // ...(※)

にしてみると、
いずれかのボタンを押すたびに

3
4
5

と出力されます。つまり、i が生き残っていることが確認できます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0