型で分岐する
結局型判別は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 = @" } }"; } }