型で分岐する

 結局型判別はisが速いなということになった。それでこんなのを作った。

public static void Switch<T1, T2, T3>(this object obj, Action<T1> t1, Action<T2> t2, Action<T3> t3, Action Default)
{
	if (obj is T1) t1((T1)obj);
	else if (obj is T2) t2((T2)obj);
	else if (obj is T3) t3((T3)obj);
	else Default();
}

 こんな風に使う。

IEnumerable<int> nums = GetCollection();

nums.Switch(
  (int[] n) => Console.WriteLine("int配列の場合"),
  (List<int> n) => Console.WriteLine("List<int>の場合"),
  (IList<int> n) => Console.WriteLine("IList<int>の場合")
  () => Console.WriteLine("どれでもなかった場合");
);

 型安全だしシンプルでなかなか書き易い。これのFuncバージョンも作った。

public static TR SwitchFunc<T1, T2, T3, TR>(this object obj, Func<T1, TR> t1, Func<T2, TR> t2, Func<T3, TR> t3)
{
	if (obj is T1) return t1((T1)obj);
	else if (obj is T2) return t2((T2)obj);
	else if (obj is T3) return t3((T3)obj);
	else throw new SwitchException("条件に合う型がありません");
}

var str = nums.SwitchFunc(
  (int[] n) => "int配列の場合",
  (List<int> n) => "List<int>の場合",
  (IList<int> n) => "IList<int>の場合",
  () => "どれでもなかった場合"
);

 これをそれぞれ1から12まで、Defaultがある場合とない場合全部作った。これで生成できる。

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

namespace CommonLib
{
	public class TypeSwitchGenerator
	{
		public static string Header = @"using System;
using System.Linq;

namespace TypeSwitch
{
	public class SwitchException : Exception { public SwitchException(string s) : base(s) { } }

	public static class Switching
	{";

		public static string GenerateSwitch(string name, int typeCount, bool Default)
		{
			var s1 = "public static void " + name + "<";

			var typeNames = Enumerable.Range(1, typeCount)
				.Select(i => "T" + i.ToString())
				.ToArray();
			var s2 = String.Join(",", typeNames);

			var s3 = ">(this object obj,";

			var actions = Enumerable.Range(1, typeCount)
				.Select(i => "Action<T" + i + "> t" + i)
				.ToArray();
			var s4 = String.Join(",", actions);

			var s5 = Default ? ", Action Default" : "";

			var s6 = @")
{
	if (obj is T1) t1((T1)obj);
";
			var elses = Enumerable.Range(2, typeCount - 1)
				.Select(i => String.Format("else if(obj is T{0}) t{0}((T{0})obj);", i))
				.ToArray();

			var s7 = String.Join("\r\n", elses);

			var s8 = Default ? "\r\n else Default();" : "\r\n";

			return s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + "}";
		}

		public static string GenerateSwitchFunc(string name, int typeCount, bool Default)
		{
			var s1 = "public static TR " + name + "<";

			var typeNames = Enumerable.Range(1, typeCount)
				.Select(i => "T" + i)
				.Concat(new[] { "TR" })
				.ToArray();

			var s2 = String.Join(",", typeNames);

			var s3 = ">(this object obj,";

			var funcs = Enumerable.Range(1, typeCount)
				.Select(i => "Func<T" + i + ", TR> t" + i)
				.ToArray();
			var s4 = String.Join(",", funcs);

			var s5 = Default ? ", Func<TR> Default" : "";

			var s6 = @")
{
	if (obj is T1) return t1((T1)obj);
";
			var elses = Enumerable.Range(2, typeCount - 1)
				.Select(i => String.Format("else if(obj is T{0}) return t{0}((T{0})obj);", i))
				.ToArray();

			var s7 = String.Join("\r\n", elses);

			var s8 = Default ?
				"\r\n else return Default();" :
				"\r\n else throw new SwitchException(\"条件に合う型がありません\");";

			return s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + "}";
		}

		public static string GenerateTypeSwitchClass()
		{
			var sb = new StringBuilder();

			sb.Append(Header);

			for (int i = 1; i <= 12; ++i)
			{
				sb.AppendLine(GenerateSwitch("Switch", i, false));
				sb.AppendLine();
				sb.AppendLine(GenerateSwitch("Switch", i, true));
				sb.AppendLine();
			}

			for (int i = 1; i <= 12; ++i)
			{
				sb.AppendLine(GenerateSwitchFunc("SwitchFunc", i, false));
				sb.AppendLine();
				sb.AppendLine(GenerateSwitchFunc("SwitchFunc", i, true));
				sb.AppendLine();
			}

			sb.Append(Footer);

			return sb.ToString();
		}

		public static string Footer = @"
	}
}";
	}
}