Delegates Generation

CSharp · Console

/Program.cs

using System;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Reflection.Emit;

public static class Programm
{
	const int rounds = 100000;
	
	public static void Main()
	{
		var boolPlain = new BoolPlain{ TypedValue = true };
		
		BenchmarkTools.MeasurePerformance("Plain", () =>
		{
			for (var i = 0; i < rounds; i++)
			{
				boolPlain.As<int>(out _);
			}
		});
		
	}
}

public class BoolPlain : ValuePlain<bool>
{
    private static readonly IReadOnlyDictionary<Type, Delegate> k_Conversions = new Dictionary<Type, Delegate>
    {
        { typeof(int),    new Func<bool, int>(value => value ? 1 : 0) },
        { typeof(float),  new Func<bool, float>(value => value ? 1.0f : 0.0f) },
        { typeof(double), new Func<bool, double>(value => value ? 1.0 : 0.0) }
    };

    protected override IReadOnlyDictionary<Type, Delegate> Conversions => k_Conversions;
}

public abstract class ValuePlain<T> : IValue
{
	private T m_Value;
	public T TypedValue { get => m_Value; set => m_Value = value; }

	public bool As<TTarget>(out TTarget result)
	{
		if (m_Value is TTarget target)
		{
			result = target;
			return true;
		}

		if (Conversions.TryGetValue(typeof(TTarget), out var conversion) && conversion is Func<T, TTarget> converter)
		{
			result = converter(TypedValue);
			return true;
		}

		if (k_DefaultConversions.TryGetValue(typeof(TTarget), out var defaultConversion))
		{
			result = (TTarget)defaultConversion(TypedValue);
			return true;
		}

		result = default;
		return false;
	}

	private static readonly IReadOnlyDictionary<Type, Func<T, object>> k_DefaultConversions = new Dictionary<Type, Func<T, object>>
	{
		{
			typeof(string),
			value => value.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    ");
		
		// Warm-up
		action();
		
		GC.Collect();
		GC.WaitForPendingFinalizers();
		GC.Collect();
		
		var stopwatch = Stopwatch.StartNew();
		action();
		stopwatch.Stop();
		stringBuilder.Append($"    Execution Time: {stopwatch.ElapsedMilliseconds} ms\n");
		
		var memoryBefore = GC.GetTotalMemory(true);
		action();
		var memoryAfter = GC.GetTotalMemory(false);
		stringBuilder.Append($"    Allocations: {NicifyMemory(memoryAfter - memoryBefore)}\n");
		
		Console.WriteLine(stringBuilder.ToString());
	}

	private static string NicifyMemory(long bytes)
	{
		return bytes switch
		{
			< 1024 => $"{bytes} B",
			< 1024 * 1024 => $"{bytes / 1024} KB",
			_ => $"{bytes / (1024 * 1024)} MB"};
	}
}

public interface IValue
{
	bool As<TTarget>(out TTarget result);
}
;