/// <summary> /// 生成指定类型的 Switch 代码块,使用 Hash 匹配算法。 /// </summary> /// <typeparam name="T">指定类型</typeparam> /// <param name="ilGen">ILGenerator IL 指令生成器</param> /// <param name="emitLoadValue">生成加载 Switch 参数的指令的委托</param> /// <param name="comparer">哈希比较器</param> /// <param name="cases">case 标签块集合</param> /// <param name="defaultLabel">默认标签块</param> /// <param name="emitLoadItem">生成加载指定 Case 块值的指定的委托</param> public static void HashSwitch <T>(this ILGenerator ilGen, Action <ILGenerator> emitLoadValue, Action <ILGenerator, T> emitLoadItem, IHashComparer <T> comparer, CaseInfo <T>[] cases, Label defaultLabel) { cases = (CaseInfo <T>[])cases.Clone(); foreach (var item in cases) { item.HashCode = comparer.GetHashCode(item.Value); } Array.Sort(cases); var groupedCases = new Dictionary <int, List <CaseInfo <T> > >(); foreach (var item in cases) { groupedCases.TryGetValue(item.HashCode, out var items); if (items is null) { items = new List <CaseInfo <T> > { item }; groupedCases.Add(item.HashCode, items); } else { items.Add(item); } } var hashCodeLocal = ilGen.DeclareLocal(typeof(int)); emitLoadValue(ilGen); comparer.EmitGetHashCode(ilGen); ilGen.StoreLocal(hashCodeLocal); SwitchObject( ilGen, emitLoadValue, il => il.LoadLocal(hashCodeLocal), emitLoadItem, comparer, groupedCases.ToList(), defaultLabel, 0, (groupedCases.Count - 1) / 2, groupedCases.Count - 1); }
private static void SwitchObject <T>(this ILGenerator ilGen, Action <ILGenerator> emitLoadValue, Action <ILGenerator> emitLoadHashCode, Action <ILGenerator, T> emitLoadItem, IHashComparer <T> comparer, List <KeyValuePair <int, List <CaseInfo <T> > > > cases, Label defaultLabel, int begin, int index, int end) { if (begin > end) { return; } if (begin == end) { emitLoadHashCode(ilGen); ilGen.LoadConstant(cases[begin].Key); ilGen.BranchIfNotEqualUnsigned(defaultLabel); if (SwitchDoNotVerify && cases[begin].Value.Count == 1) { ilGen.Branch(cases[begin].Value[0].Label); } else { foreach (var item in cases[begin].Value) { emitLoadValue(ilGen); emitLoadItem(ilGen, item.Value); comparer.EmitEquals(ilGen); ilGen.BranchTrue(item.Label); } } ilGen.Branch(defaultLabel); return; } if (begin + 1 == end) { var endLabel = ilGen.DefineLabel(); emitLoadHashCode(ilGen); ilGen.LoadConstant(cases[begin].Key); ilGen.BranchIfNotEqualUnsigned(endLabel); if (SwitchDoNotVerify && cases[begin].Value.Count == 1) { ilGen.Branch(cases[begin].Value[0].Label); } else { foreach (var item in cases[begin].Value) { emitLoadValue(ilGen); emitLoadItem(ilGen, item.Value); comparer.EmitEquals(ilGen); ilGen.BranchTrue(item.Label); } } ilGen.MarkLabel(endLabel); emitLoadHashCode(ilGen); ilGen.LoadConstant(cases[end].Key); ilGen.BranchIfNotEqualUnsigned(defaultLabel); if (SwitchDoNotVerify && cases[end].Value.Count == 1) { ilGen.Branch(cases[end].Value[0].Label); } else { foreach (var item in cases[end].Value) { emitLoadValue(ilGen); emitLoadItem(ilGen, item.Value); comparer.EmitEquals(ilGen); ilGen.BranchTrue(item.Label); } } ilGen.Branch(defaultLabel); return; } var greaterLabel = ilGen.DefineLabel(); emitLoadHashCode(ilGen); ilGen.LoadConstant(cases[index].Key); ilGen.BranchIfGreater(greaterLabel); SwitchObject( ilGen, emitLoadValue, emitLoadHashCode, emitLoadItem, comparer, cases, defaultLabel, begin, (begin + index) / 2, index); ilGen.MarkLabel(greaterLabel); SwitchObject( ilGen, emitLoadValue, emitLoadHashCode, emitLoadItem, comparer, cases, defaultLabel, index + 1, (index + 1 + end) / 2, end); }