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

TypeMatch (邪悪です)

C# Scala

先週末の名古屋 Scala 勉強会用に、こんなコードを書きました。
2 行目からの ObjectExtension クラスで、object に対して拡張メソッドを追加しています*1
このときは Cast という名前にしましたが、これは TypeMatch という名前にして、

e.TypeMatch(
    (Var v)    => v.ToString(),
    (Number n) => n.ToString(),
    (UnOp u)   => u.Operator != "-" ? u.ToString()
                                    : u.Arg.TypeMatch(
                                          (UnOp uu) => uu.Operator == "-" ? uu.Arg.ToString()
                                                                          : string.Format("-({0})", uu),
                                          () => u.ToString()
                                      ),
    (BinOp b)  => b.ToString()
)

と書いたほうがそれっぽいですね。
後はいつもどおり、T4 Template で自動生成すれば OK です。
以下その例です。

<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" debug="true" hostSpecific="true" #>
<#@ output extension=".cs" encoding="UTF-8" #>
<#@ Assembly Name="System.dll" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="System.Windows.Forms.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#
int n = 10;
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonsSharp.Lang
{
    /// <summary>
    /// objectの拡張メソッド群です。
    /// </summary>
    public static class ObjectExtension
    {
<#
for (int i = 1; i <= n; i++) {
    var range = Enumerable.Range(1, i);
    var types = range.Select(j => "T" + j).ToArray();
    var args1 = range.Select(j => string.Format("Action<T{0}> ifT{0}", j)).ToArray();
    var args2 = range.Select(j => string.Format("Func<T{0}, TResult> ifT{0}", j)).ToArray();

    var typeParam = string.Join(", ", types);
    var actParam = string.Join(", ", args1);
    var funcParam = string.Join(", ", args2);
#>
public static void TypeMatch<<#= typeParam #>>(this object self, <#= actParam #>)
        {
            if (self is T1)
            {
                ifT1((T1)self);
                return;
            }
<#
    for (int j = 2; j <= i; j++) {
#>
            else if (self is T<#= j #>)
            {
                ifT<#= j #>((T<#= j #>)self);
                return;
            }
<#
    }
#>
            throw new Exception();
        }

        public static void TypeMatch<<#= typeParam #>>(this object self, <#= actParam #>, Action otherwise)
        {
            if (self is T1)
                ifT1((T1)self);
<#
    for (int j = 2; j <= i; j++) {
#>
            else if (self is T<#= j #>)
                ifT<#= j #>((T<#= j #>)self);
<#
    }
#>
            else
                otherwise();
        }

        public static TResult TypeMatch<<#= typeParam #>, TResult>(this object self, <#= funcParam #>)
        {
            if (self is T1)
                return ifT1((T1)self);
<#
    for (int j = 2; j <= i; j++) {
#>
            else if (self is T<#= j #>)
                return ifT<#= j #>((T<#= j #>)self);
<#
    }
#>
            throw new Exception();
        }

        public static TResult TypeMatch<<#= typeParam #>, TResult>(this object self, <#= funcParam #>, Func<TResult> otherwise)
        {
            if (self is T1)
                return ifT1((T1)self);
<#
    for (int j = 2; j <= i; j++) {
#>
            else if (self is T<#= j #>)
                return ifT<#= j #>((T<#= j #>)self);
<#
    }
#>
            return otherwise();
        }
        
<# } #>
    }
}

*1:この辺が邪悪。System.Object に拡張するのは出来る限り避けるべき・・・らしい。VB とか使わないので知りません!