読者です 読者をやめる 読者になる 読者になる

One-liner 言語としての C# 3.0 はきっとその域に達している

.NET

id:siokoshou:20070901#p2 がおもしろかったので C# コンパイラのメソッドルックアップをチェックするコードを書いてみました.

(from mi in new MethodInfo[]{ Reflector.Function<string>.Bind((Base _b, string _s) => () => _b.Method(_s)), Reflector.Function<string>.Bind((Derived _d, string _s) => () => _d.Method(_s)), Reflector.Function<string>.Bind((Hoge _h, string _s) => () => _h.Method(_s)) } let paramTypeStrs = from pi in mi.GetParameters() select pi.ParameterType.ToString() let paramStr = paramTypeStrs.Aggregate( (accum, next) => accum + " -> " + next ) select mi.DeclaringType + "." + mi.Name + " : " + paramStr + " -> " + mi.ReturnType).ToList().ForEach(Console.WriteLine);

嘘ですごめんなさい.普通に書くならこんな感じ.id:NyaRuRu:20070826:p2 で書いた Strong-typed reflection も再掲.

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var funcs = new MethodInfo[]
        {
            Reflector.Function<string>.Bind((Base _b, string _s) => () => _b.Method(_s)),
            Reflector.Function<string>.Bind((Derived _d, string _s) => () => _d.Method(_s)),
            Reflector.Function<string>.Bind((Hoge _h, string _s) => () => _h.Method(_s))
        };
 
        var seq = from mi in funcs 
                  let paramTypeStrs = from pi in mi.GetParameters()
                                      select pi.ParameterType.ToString()
                  let paramStr = paramTypeStrs.Aggregate((accum, next) => accum + " -> " + next)
                  select mi.DeclaringType + "." + mi.Name + " : " + paramStr + " -> " + mi.ReturnType;

               // (追記) 上の文字列連結はこちらの方がベター?
               // let paramStr = String.Join( " -> ", paramTypeStrs.ToArray() )

        seq.ToList().ForEach(Console.WriteLine);

        Console.ReadKey();
    }
}

public class Base
{
    public virtual string Method(string s)
    {
        return "Base.Method(string)";
    }
}

public class Derived : Base
{
    public override string Method(string s)
    {
        return "Derived.Method(string)";
    }

    public virtual string Method(object o)
    {
        return "Derived.Method(object)";
    }
}

public class Hoge
{
    public string Method(string s)
    {
        return "Hoge.Method(string)";
    }

    public string Method(object o)
    {
        return "Hoge.Method(object)";
    }
}

public static class Reflector
{
    static MethodInfo GetMethodInfo(this Expression expression)
    {
        var findfunc = null as Func<Expression, MethodInfo>;
        findfunc = expr =>
        {
            switch (expr.NodeType)
            {
                case ExpressionType.Lambda:
                    return findfunc((expr as LambdaExpression).Body);
                case ExpressionType.Quote:
                    return findfunc((expr as UnaryExpression).Operand);
                case ExpressionType.Call:
                    var mce = expr as MethodCallExpression;
                    return mce != null ? mce.Method : null;
                case ExpressionType.MemberAccess:
                    var me = expr as MemberExpression;
                    var pi = me.Member as PropertyInfo;
                    return pi != null ? pi.GetGetMethod(true) : null;
                default:
                    return null;
            }
        };

        return findfunc(expression);
    }
    public sealed class Function<TResult>
    {
        public static MethodInfo Bind(
            Expression<Func<Expression<Func<TResult>>>> expr)
        {
            return expr.GetMethodInfo();
        }
        public static MethodInfo Bind<TArg0>(
            Expression<Func<TArg0, Expression<Func<TResult>>>> expr)
        {
            return expr.GetMethodInfo();
        }
        public static MethodInfo Bind<TArg0, TArg1>(
            Expression<Func<TArg0, TArg1, Expression<Func<TResult>>>> expr)
        {
            return expr.GetMethodInfo();
        }
        public static MethodInfo Bind<TArg0, TArg1, TArg2>(
            Expression<Func<TArg0, TArg1, TArg2, Expression<Func<TResult>>>> expr)
        {
            return expr.GetMethodInfo();
        }
    }
    public sealed class Function
    {
        public static MethodInfo Bind<TArg0>(
            Expression<Func<TArg0, Expression<Action>>> expr)
        {
            return expr.GetMethodInfo();
        }
        public static MethodInfo Bind<TArg0, TArg1>(
            Expression<Func<TArg0, TArg1, Expression<Action>>> expr)
        {
            return expr.GetMethodInfo();
        }
        public static MethodInfo Bind<TArg0, TArg1, TArg2>(
            Expression<Func<TArg0, TArg1, TArg2, Expression<Action>>> expr)
        {
            return expr.GetMethodInfo();
        }
        public static MethodInfo Bind<TArg0, TArg1, TArg2, TArg3>(
            Expression<Func<TArg0, TArg1, TArg2, TArg3, Expression<Action>>> expr)
        {
            return expr.GetMethodInfo();
        }
    }
}

LINQ 構文でだらだらと書いて改行潰せばワンライナーなので,それなりにその域には達しちゃった言語なのかもしれません.まあ LISP みたいなのは別格としても,C あたりの構文からスタートして,よくぞここまで練り上げた,と.
LINQ 構文のために大量に簡約ルールを追加したのもありますが,裏方サポートとして,id:NyaRuRu:20070827:p2 で書いたように「式」が増えたのと,Extension Method の導入によって method chain が楽になったのも大きいかな.
とはいえ,コマンドラインから直接叩くことを考えると,C# では参照設定や using 前提なのが邪魔だったりするのかも.