複雑なラムダ式

最近、ASP.NETMVCのソースコードを読んでいるのだが、そこでは複雑なラムダ式が多々でてくる。
今回はその一例としてアクションフィルタの処理(ControllerActionInvokerクラスのInvokeActionMethodWithFiltersメソッド)を自分なりに解釈し、コードを起こしたものを載せておく。

いくつか有用そうなテクニックを盗めたし、
ASP.NET MVCソースコードは本当に勉強になります。

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

namespace LinqSample
{
    public interface IActionParameter
    {
        object GetParameter();
    }

    public class GenericParameter<T> : IActionParameter
    {
        private T val;

        public GenericParameter(T val)
        {
            this.val = val;
        }

        public object GetParameter()
        {
            return val;
        }
    }

    public class ActionResult
    {
        public object Value { get; set; }
    }

    public class ActionContext
    {
        public ActionResult CurrentValue { get; set; }

        public ActionContext()
        {
            CurrentValue = new ActionResult() { Value = "" };
        }
    }

    public static class Action
    {
        public static ActionResult ConcatString(Func<ActionResult> action, IActionParameter param)
        {
            ActionResult current = action();
            var newValue = current.Value.ToString() + param.GetParameter().ToString();
            current.Value = newValue;
            Console.WriteLine(current.Value);
          
            return current;
        }

        public static ActionResult GetMaxValue(Func<ActionResult> action, IActionParameter param)
        {
            int tpResult;

            if (!int.TryParse(param.GetParameter().ToString(), out tpResult))
                throw new ArgumentException("パラメーター不正(param)");

            var current = action();

            if (!int.TryParse(current.Value.ToString(), out tpResult))
                throw new ArgumentException("パラメーター不正(action)");

            var newValue = "";

            if (Convert.ToInt32(current.Value) >= Convert.ToInt32(param.GetParameter()))
                newValue = current.Value.ToString();
            else
                newValue = param.GetParameter().ToString();

            Console.WriteLine("現在値:{0}、比較値:{1}  =>  {2}を適用", current.Value, param.GetParameter(), newValue);
            current.Value = newValue;
            return current;
        }
    }

    public class ActionProcesser
    {
        public void DoAction(Func<ActionResult> acumrator, Func<Func<ActionResult>, IActionParameter, Func<ActionResult>> func)
        {
            var parameters = new List<IActionParameter>();
            parameters.Add(new GenericParameter<int>(10));
            parameters.Add(new GenericParameter<int>(20));
            parameters.Add(new GenericParameter<int>(30));
            parameters.Add(new GenericParameter<int>(40));
            parameters.Add(new GenericParameter<int>(50));
            parameters.Add(new GenericParameter<int>(60));
            parameters.Add(new GenericParameter<int>(70));
            parameters.Add(new GenericParameter<int>(80));
            parameters.Add(new GenericParameter<int>(90));
            parameters.Add(new GenericParameter<int>(100));

            /*
             * 第1引数には、アキュムレーター値を返すFuncデリゲートを指定。
             * 第2引数(アキュムレーター関数)には、アキュムレーターと同タイプのFuncを返すデリゲートを指定。
             * こうすることで各パラメーターを外出しすることが可能になる。
             * 
             * アキュムレーター関数にデリゲートを渡すと即時実行されないようなので、最後に()をつけることでInvokeしている。
             */
            parameters.Aggregate(acumrator, (a, p) => func(a, p))();
        }

        public void DoConcatString()
        {
            /*
             * アキュムレーターが保持する値をアキュムレーター自身が保持できない(デリゲートだから)
             * そこで、値保持用のContextクラスを使用することで対応した。
             * 
             */

            var context = new ActionContext();

            Func<ActionResult> acumrator = () =>
            {
                return context.CurrentValue;
            };

            /*
             * 実際の処理呼び出し。
             * ラムダ式の中で、アキュムレーター関数の実処理を呼んでいる。
             * こうすることで実処理のテスト容易になる。
             * (これが一番の狙いか?)
             */
            DoAction(acumrator, (a, p) => () => Action.ConcatString(a, p));
        }

        public void DoCheckMaxValue()
        {
            var context = new ActionContext();

            Func<ActionResult> acumrator = () =>
            {
                context.CurrentValue.Value = "0";
                return context.CurrentValue;
            };

            DoAction(acumrator, (a, p) => () => Action.GetMaxValue(a, p));
        }
    }
}


Action.ConcatStringのテストコード。

[TestClass()]
public class ActionTest
{
        /// <summary>
        ///ConcatString のテスト
        ///</summary>
        [TestMethod()]
        public void ConcatStringTest()
        {
            Func<ActionResult> action = () => new ActionResult() { Value = "やまだ" };
            IActionParameter param = new GenericParameter<string>("たろう");
            ActionResult expected = new ActionResult() { Value = "やまだたろう" };
            ActionResult actual;
            actual = LinqSample.Action.ConcatString(action, param);
            Assert.AreEqual(expected.Value, actual.Value);
        }

        /// <summary>
        ///GetMaxValue のテスト
        ///</summary>
        [TestMethod()]
        public void GetMaxValueTest()
        {
            Func<ActionResult> action = () => new ActionResult() { Value = "100" };
            IActionParameter param = new GenericParameter<int>(99);
            ActionResult expected = new ActionResult() { Value = "100" };
            ActionResult actual;
            actual = LinqSample.Action.GetMaxValue(action, param);
            Assert.AreEqual(expected.Value, actual.Value);
        }
}