using System.Linq.Expressions;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Emit;
public static class Programm
const int rounds = 100000;
public static void Main()
var boolPlain = new BoolPlain{ TypedValue = true };
var boolLinqExp = new BoolValueLinqExp{ TypedValue = true};
var boolReflectionEmit = new BoolValueReflectionEmit { TypedValue = true };
BenchmarkTools.MeasurePerformance("Plain", () =>
for (var i = 0; i < rounds; i++)
boolPlain.As<int>(out _);
BenchmarkTools.MeasurePerformance("LinqExp", () =>
for (var i = 0; i < rounds; i++)
boolLinqExp.As<int>(out _);
BenchmarkTools.MeasurePerformance("ReflectionEmit", () =>
for (var i = 0; i < rounds; i++)
boolReflectionEmit.As<int>(out _);
public class BoolPlain : ValuePlain<bool>
private static readonly IReadOnlyDictionary<Type, Func<bool, object>> k_Conversions = new Dictionary<Type, Func<bool, object>>
value => value ? 1.0f : 0.0f
value => value ? 1.0 : 0.0
protected override IReadOnlyDictionary<Type, Func<bool, object>> Conversions => k_Conversions;
public abstract class ValuePlain<T> : IValue
public T TypedValue { get => m_Value; set => m_Value = value; }
public bool As<TTarget>(out TTarget result)
if (m_Value is TTarget target)
if (Conversions.TryGetValue(typeof(TTarget), out var conversion))
result = (TTarget)conversion(TypedValue);
if (k_DefaultConversions.TryGetValue(typeof(TTarget), out var defaultConversion))
result = (TTarget)defaultConversion(TypedValue);
private static readonly IReadOnlyDictionary<Type, Func<T, object>> k_DefaultConversions = new Dictionary<Type, Func<T, object>>
value => value.ToString()
protected abstract IReadOnlyDictionary<Type, Func<T, object>> Conversions { get; }
public class BoolValueLinqExp : ValueLinqExp<bool>
private static readonly IReadOnlyDictionary<Type, Delegate> k_Conversions = new Dictionary<Type, Delegate>
{typeof(int), CreateConverter<int>(v => v ? 1 : 0)},
{typeof(float), CreateConverter<float>(v => v ? 1.0f : 0.0f)},
{typeof(double), CreateConverter<double>(v => v ? 1.0 : 0.0)}
protected override IReadOnlyDictionary<Type, Delegate> Conversions => k_Conversions;
public class BoolValueReflectionEmit : ValueLinqExp<bool>
private static readonly IReadOnlyDictionary<Type, Delegate> k_Conversions = new Dictionary<Type, Delegate>
{typeof(int), CreateConverterWithEmit<int>(v => v ? 1 : 0)},
{typeof(float), CreateConverterWithEmit<float>(v => v ? 1.0f : 0.0f)},
{typeof(double), CreateConverterWithEmit<double>(v => v ? 1.0 : 0.0)}
protected override IReadOnlyDictionary<Type, Delegate> Conversions => k_Conversions;
public abstract class ValueLinqExp<T> : IValue
public T TypedValue { get => m_Value; set => m_Value = value; }
public bool As<TTarget>(out TTarget result)
if (m_Value is TTarget target)
if (Conversions.TryGetValue(typeof(TTarget), out var conversion) && conversion is Func<T, TTarget> converter)
result = converter(TypedValue);
if (k_DefaultConversions.TryGetValue(typeof(TTarget), out var defaultConversion) && defaultConversion is Func<T, TTarget> defaultConverter)
result = defaultConverter(TypedValue);
protected static Delegate CreateConverter<TOutput>(Func<T, TOutput> converter)
var input = Expression.Parameter(typeof(T), "input");
var body = Expression.Invoke(Expression.Constant(converter), input);
var lambda = Expression.Lambda(body, input);
protected static Delegate CreateConverterWithEmit<TOutput>(Func<T, TOutput> converter)
var method = new DynamicMethod(
"Convert_" + typeof(T).Name + "_To_" + typeof(TOutput).Name,
typeof(ValueLinqExp<>).Module,
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Callvirt, converter.Method, null);
return method.CreateDelegate(typeof(Func<T, TOutput>));
private static readonly IReadOnlyDictionary<Type, Delegate> k_DefaultConversions = new Dictionary<Type, Delegate>
CreateConverter<string>(v => v.ToString())
protected abstract IReadOnlyDictionary<Type, Delegate> Conversions { get; }
public static class BenchmarkTools
public static void MeasurePerformance(string approach, Action action)
var stringBuilder = new StringBuilder($"Performance Measurement: {approach}\n ");
GC.WaitForPendingFinalizers();
var stopwatch = Stopwatch.StartNew();
stringBuilder.Append($" Execution Time: {stopwatch.ElapsedMilliseconds} ms\n");
var memoryBefore = GC.GetTotalMemory(true);
var memoryAfter = GC.GetTotalMemory(false);
stringBuilder.Append($" Allocations: {NicifyMemory(memoryAfter - memoryBefore)}\n");
Console.WriteLine(stringBuilder.ToString());
private static string NicifyMemory(long bytes)
< 1024 * 1024 => $"{bytes / 1024} KB",
_ => $"{bytes / (1024 * 1024)} MB"};
bool As<TTarget>(out TTarget result);