Skip to content

Instantly share code, notes, and snippets.

@pwelter34
Last active May 22, 2024 18:27
Show Gist options
  • Save pwelter34/f6ae744f6a913cdc52ebe186edac0f22 to your computer and use it in GitHub Desktop.
Save pwelter34/f6ae744f6a913cdc52ebe186edac0f22 to your computer and use it in GitHub Desktop.
TypeSwitch - Switch on runtime type
[MemoryDiagnoser]
public class BenchmarkSwitch
{
[Params(typeof(int), typeof(DateTimeOffset), typeof(TimeSpan))]
public Type TargetType { get; set; } = typeof(int);
[Benchmark(Baseline = true)]
public int BaseLineBenchmark()
{
if (TargetType == typeof(string))
return 1;
if (TargetType == typeof(int))
return 2;
if (TargetType == typeof(long))
return 3;
if (TargetType == typeof(short))
return 4;
if (TargetType == typeof(float))
return 5;
if (TargetType == typeof(double))
return 6;
if (TargetType == typeof(decimal))
return 7;
if (TargetType == typeof(DateTime))
return 8;
if (TargetType == typeof(DateTimeOffset))
return 9;
if (TargetType == typeof(DateOnly))
return 10;
if (TargetType == typeof(TimeOnly))
return 11;
if (TargetType == typeof(Guid))
return 12;
return 99;
}
[Benchmark]
public int SwitchBenchmark()
{
int value = 0;
TargetType
.Switch()
.Case<string>(() => value = 1)
.Case<int>(() => value = 2)
.Case<long>(() => value = 3)
.Case<short>(() => value = 4)
.Case<float>(() => value = 5)
.Case<double>(() => value = 6)
.Case<decimal>(() => value = 7)
.Case<DateTime>(() => value = 8)
.Case<DateTimeOffset>(() => value = 9)
.Case<DateOnly>(() => value = 10)
.Case<TimeOnly>(() => value = 11)
.Case<Guid>(() => value = 12)
.Default(() => value = 99);
return value;
}
}
public class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run(typeof(Program).Assembly);
}
}
Method TargetType Mean Error StdDev Gen0 Allocated
BaseLineBenchmark DateTimeOffset 1.3773 ns 0.0099 ns 0.0077 ns - -
SwitchBenchmark DateTimeOffset 83.2784 ns 2.4676 ns 7.2370 ns 0.1023 856 B
BaseLineBenchmark Int32 0.0266 ns 0.0139 ns 0.0130 ns - -
SwitchBenchmark Int32 72.3950 ns 2.0014 ns 5.7744 ns 0.1023 856 B
BaseLineBenchmark TimeSpan 2.1881 ns 0.0305 ns 0.0286 ns - -
SwitchBenchmark TimeSpan 72.3910 ns 1.7300 ns 4.9358 ns 0.1023 856 B
public static class TypeSwitch
{
// switch case that indicates there was already a match
private static readonly SwitchCase Matched = new(typeof(SwitchCase));
public static SwitchCase Switch<T>()
{
var type = typeof(T);
return new SwitchCase(type);
}
public static SwitchCase Switch(this Type type)
{
if (type is null)
throw new ArgumentNullException(nameof(type));
return new SwitchCase(type);
}
public static SwitchCase Case<T>(this SwitchCase switchCase, Action action)
{
if (action is null)
throw new ArgumentNullException(nameof(action));
// already matched, short circuit
if (switchCase == Matched)
return switchCase;
var type = typeof(T);
return Case(switchCase, type, action);
}
public static SwitchCase Case(this SwitchCase switchCase, Type type, Action action)
{
if (type is null)
throw new ArgumentNullException(nameof(type));
if (action is null)
throw new ArgumentNullException(nameof(action));
// already matched, short circuit
if (switchCase == Matched)
return switchCase;
if (type != switchCase.Type)
return switchCase;
// invoke action and return matched
action();
return Matched;
}
public static SwitchCase Default(this SwitchCase switchCase, Action action)
{
if (action is null)
throw new ArgumentNullException(nameof(action));
// already matched
if (switchCase == Matched)
return switchCase;
// invoke action and return matched
action();
return Matched;
}
public readonly record struct SwitchCase(Type Type);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment