C#でEval

http://gyazo.com/7f9ebdb929837fa51c96b52d162ea463.png

CSharpCodeProviderを使うとC#プログラム上でC#のコードをコンパイルして動作させる,要するにEvalみたいなことができる.で,アセンブリ参照に自分自身を指定してコンパイルすることで,Eval先から自分のpublic classを参照することができる.ということは,自分自身では抽象クラスだけ書いておき,Eval先で実装を埋めるという動的な(?)template methodパターンが作れる.
これは面白いなー有意義な使い道ないかなーということで,画像処理の簡単なテストが出来るアプリを書いてみた.

https://github.com/nyanp/FilterEval

やってることは超簡単で,まずコア関数をtemplateにした抽象フィルタを書く.

    public abstract class AbstractFilter
    {
        private Bitmap src;

        public AbstractFilter(Bitmap image)
        {
            src = image;
        }

        public Bitmap Execute()
        {
            Bitmap dst = src.Clone(new Rectangle(0, 0, src.Width, src.Height), src.PixelFormat);

            for (int y = 0; y < b.Height; y++)
            {
                for (int x = 0; x < b.Width; x++)
                {
                    dst.SetPixel(x, y, Filter(src, x, y));
                }
            }

            return dst;
        }

        protected abstract Color Filter(Bitmap image, int x, int y);
    }

そしたらあとはTextBoxから文字列を取ってきて,Eval用の文字列とくっつけたらそれをコンパイル.これだけ.CSharpCodeEval.Evalは簡単なヘルパ関数.code projectに参考になるコードがあった.
Evaluate C# Code (Eval Function) - CodeProject

        private Bitmap EvalFilter()
        {
            return (Bitmap)CSharpCodeEval.Eval(CreateFilterCode(textBox1.Text), "EvalTest.Evaler", "EvalCode", new object[1] { imageHistory[shownImageIndex] });
        }

        private string CreateFilterCode(string coreCode)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Xml;");
            sb.AppendLine("using System.Data;");
            sb.AppendLine("using System.Drawing;");
            sb.AppendLine("namespace EvalTest{ ");
            sb.AppendLine("public class EvalFilter : AbstractFilter { ");
            sb.AppendLine("public EvalFilter(Bitmap image) : base(image){ }");
            sb.AppendLine("protected override Color Filter(BitmapPlus image, int x, int y) {");
            sb.AppendLine(coreCode);
            sb.AppendLine("} ");
            sb.AppendLine("} ");
            sb.AppendLine("public class Evaler {");
            sb.AppendLine("public object EvalCode(object image) {");
            sb.AppendLine("EvalFilter e = new EvalFilter((Bitmap)image);");
            sb.AppendLine("return e.Execute();");
            sb.AppendLine("}");
            sb.AppendLine("} ");
            sb.AppendLine("} ");
            return sb.ToString();
        }

Bitmap.Getpixelはやたら遅いし,範囲外アクセスに対してケアするコードをEval側で書くのが正直面倒だったので,生配列にアクセスする以下の実装を貰い,ついでに範囲外アクセスを無理やり範囲内に矯正するAt(x,y)を追加.
NonSoft - Bitmap処理を高速化するサンプル(C#.NET)

ラプラシアンでもガウシアンでもぼくの考えたさいきょうのフィルタでもさくっと書いて試せるので結構便利.