diff --git a/csharp/Platform.Data.Doublets.Benchmarks/DynamicVsGenericBenchmarks.cs b/csharp/Platform.Data.Doublets.Benchmarks/DynamicVsGenericBenchmarks.cs
new file mode 100644
index 000000000..333c597d1
--- /dev/null
+++ b/csharp/Platform.Data.Doublets.Benchmarks/DynamicVsGenericBenchmarks.cs
@@ -0,0 +1,211 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Numerics;
+using BenchmarkDotNet.Attributes;
+
+#pragma warning disable CA1822 // Mark members as static
+
+namespace Platform.Data.Doublets.Benchmarks
+{
+ ///
+ /// Performance comparison between different approaches for generic operations.
+ /// Based on https://stackoverflow.com/a/8122675/710069
+ ///
+ [SimpleJob]
+ [MemoryDiagnoser]
+ public class DynamicVsGenericBenchmarks
+ {
+ private const int Iterations = 1000;
+ private readonly ulong[] _values;
+
+ public DynamicVsGenericBenchmarks()
+ {
+ _values = new ulong[Iterations];
+ var random = new System.Random(42);
+ for (int i = 0; i < Iterations; i++)
+ {
+ _values[i] = (ulong)random.Next(1, 100);
+ }
+ }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ // Warm up expression compilation
+ ExpressionAdd(1UL, 2UL);
+ ExpressionEquals(1UL, 2UL);
+ }
+
+ [Benchmark(Description = "INumber Addition")]
+ public ulong INumberAddition()
+ {
+ ulong sum = 0;
+ for (int i = 0; i < Iterations - 1; i++)
+ {
+ sum += INumberAdd(_values[i], _values[i + 1]);
+ }
+ return sum;
+ }
+
+ [Benchmark(Description = "Dynamic Addition")]
+ public ulong DynamicAddition()
+ {
+ ulong sum = 0;
+ for (int i = 0; i < Iterations - 1; i++)
+ {
+ sum += DynamicAdd(_values[i], _values[i + 1]);
+ }
+ return sum;
+ }
+
+ [Benchmark(Description = "Expression Addition")]
+ public ulong ExpressionAddition()
+ {
+ ulong sum = 0;
+ for (int i = 0; i < Iterations - 1; i++)
+ {
+ sum += ExpressionAdd(_values[i], _values[i + 1]);
+ }
+ return sum;
+ }
+
+ [Benchmark(Description = "IEqualityOperators Equality")]
+ public int IEqualityOperatorsEquality()
+ {
+ int equalCount = 0;
+ for (int i = 0; i < Iterations - 1; i++)
+ {
+ if (IEqualityOperatorsEquals(_values[i], _values[i + 1]))
+ equalCount++;
+ }
+ return equalCount;
+ }
+
+ [Benchmark(Description = "EqualityComparer Equality")]
+ public int EqualityComparerEquality()
+ {
+ int equalCount = 0;
+ for (int i = 0; i < Iterations - 1; i++)
+ {
+ if (EqualityComparerEquals(_values[i], _values[i + 1]))
+ equalCount++;
+ }
+ return equalCount;
+ }
+
+ [Benchmark(Description = "Dynamic Equality")]
+ public int DynamicEquality()
+ {
+ int equalCount = 0;
+ for (int i = 0; i < Iterations - 1; i++)
+ {
+ if (DynamicEquals(_values[i], _values[i + 1]))
+ equalCount++;
+ }
+ return equalCount;
+ }
+
+ [Benchmark(Description = "Expression Equality")]
+ public int ExpressionEquality()
+ {
+ int equalCount = 0;
+ for (int i = 0; i < Iterations - 1; i++)
+ {
+ if (ExpressionEquals(_values[i], _values[i + 1]))
+ equalCount++;
+ }
+ return equalCount;
+ }
+
+ // Implementation methods
+
+ // INumber approach (NET 7+ - best performance)
+ private static T INumberAdd(T a, T b) where T : INumber
+ {
+ return a + b;
+ }
+
+ // Dynamic approach (slowest)
+ private static T DynamicAdd(T a, T b)
+ {
+ dynamic dynamicA = a;
+ dynamic dynamicB = b;
+ dynamic result = dynamicA + dynamicB;
+ return (T)Convert.ChangeType(result, typeof(T));
+ }
+
+ // Expression-based approach (moderate performance)
+ private static readonly ConcurrentDictionary _addFunctions =
+ new ConcurrentDictionary();
+
+ private static T ExpressionAdd(T a, T b)
+ {
+ var addFunc = (Func)_addFunctions.GetOrAdd(typeof(T), type =>
+ {
+ var paramA = Expression.Parameter(type, "a");
+ var paramB = Expression.Parameter(type, "b");
+
+ Expression left = paramA;
+ Expression right = paramB;
+
+ // For byte and other smaller types, convert to int for arithmetic
+ if (type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort))
+ {
+ left = Expression.Convert(paramA, typeof(int));
+ right = Expression.Convert(paramB, typeof(int));
+ }
+
+ Expression body = Expression.Add(left, right);
+
+ // Convert back if needed
+ if (type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort))
+ {
+ body = Expression.Convert(body, type);
+ }
+
+ return Expression.Lambda>(body, paramA, paramB).Compile();
+ });
+
+ return addFunc(a, b);
+ }
+
+ // IEqualityOperators approach (NET 7+ - best performance)
+ private static bool IEqualityOperatorsEquals(T a, T b) where T : IEqualityOperators
+ {
+ return a == b;
+ }
+
+ // EqualityComparer approach (good performance)
+ private static bool EqualityComparerEquals(T a, T b)
+ {
+ return EqualityComparer.Default.Equals(a, b);
+ }
+
+ // Dynamic approach (slowest)
+ private static bool DynamicEquals(T a, T b)
+ {
+ dynamic dynamicA = a;
+ dynamic dynamicB = b;
+ return (bool)(dynamicA == dynamicB);
+ }
+
+ // Expression-based approach (moderate performance)
+ private static readonly ConcurrentDictionary _equalityFunctions =
+ new ConcurrentDictionary();
+
+ private static bool ExpressionEquals(T a, T b)
+ {
+ var equalityFunc = (Func)_equalityFunctions.GetOrAdd(typeof(T), type =>
+ {
+ var paramA = Expression.Parameter(type, "a");
+ var paramB = Expression.Parameter(type, "b");
+ var body = Expression.Equal(paramA, paramB);
+ return Expression.Lambda>(body, paramA, paramB).Compile();
+ });
+
+ return equalityFunc(a, b);
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets.Benchmarks/Program.cs b/csharp/Platform.Data.Doublets.Benchmarks/Program.cs
index f987151c6..21397aa67 100644
--- a/csharp/Platform.Data.Doublets.Benchmarks/Program.cs
+++ b/csharp/Platform.Data.Doublets.Benchmarks/Program.cs
@@ -8,6 +8,7 @@ static void Main()
{
BenchmarkRunner.Run();
BenchmarkRunner.Run();
+ BenchmarkRunner.Run();
// BenchmarkRunner.Run();
}
}
diff --git a/csharp/Platform.Data.Doublets.Tests/ComparisonTests.cs b/csharp/Platform.Data.Doublets.Tests/ComparisonTests.cs
new file mode 100644
index 000000000..b0bca6f06
--- /dev/null
+++ b/csharp/Platform.Data.Doublets.Tests/ComparisonTests.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Numerics;
+using System.Linq.Expressions;
+using Xunit;
+using Platform.Converters;
+
+namespace Platform.Data.Doublets.Tests
+{
+ public static class ComparisonTests
+ {
+ [Fact]
+ public static void GenericAdditionTest()
+ {
+ // Test INumber approach (NET 7+) - recommended approach
+ TestINumberAddition();
+ TestINumberAddition();
+ TestINumberAddition();
+ TestINumberAddition();
+
+ // Test dynamic approach - for comparison
+ TestDynamicAddition();
+ TestDynamicAddition();
+ TestDynamicAddition();
+ TestDynamicAddition();
+
+ // Test expression-based approach
+ TestExpressionAddition();
+ TestExpressionAddition();
+ TestExpressionAddition();
+ TestExpressionAddition();
+ }
+
+ private static void TestINumberAddition() where T : INumber
+ {
+ var a = T.One;
+ var b = T.One;
+ var result = INumberAdd(a, b);
+ var expected = T.One + T.One;
+ Assert.Equal(expected, result);
+ }
+
+ private static void TestDynamicAddition()
+ {
+ var a = GetOne();
+ var b = GetOne();
+ var result = DynamicAdd(a, b);
+ var expected = GetTwo();
+ Assert.Equal(expected, result);
+ }
+
+ private static void TestExpressionAddition()
+ {
+ var a = GetOne();
+ var b = GetOne();
+ var result = ExpressionAdd(a, b);
+ var expected = GetTwo();
+ Assert.Equal(expected, result);
+ }
+
+ // INumber approach (NET 7+ - best performance)
+ private static T INumberAdd(T a, T b) where T : INumber
+ {
+ return a + b;
+ }
+
+ // Dynamic approach (slowest)
+ private static T DynamicAdd(T a, T b)
+ {
+ dynamic dynamicA = a;
+ dynamic dynamicB = b;
+ dynamic result = dynamicA + dynamicB;
+ return (T)Convert.ChangeType(result, typeof(T));
+ }
+
+ // Expression-based approach (moderate performance)
+ private static readonly System.Collections.Concurrent.ConcurrentDictionary _addFunctions =
+ new System.Collections.Concurrent.ConcurrentDictionary();
+
+ private static T ExpressionAdd(T a, T b)
+ {
+ var addFunc = (Func)_addFunctions.GetOrAdd(typeof(T), type =>
+ {
+ var paramA = Expression.Parameter(type, "a");
+ var paramB = Expression.Parameter(type, "b");
+
+ Expression left = paramA;
+ Expression right = paramB;
+
+ // For byte and other smaller types, convert to int for arithmetic
+ if (type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort))
+ {
+ left = Expression.Convert(paramA, typeof(int));
+ right = Expression.Convert(paramB, typeof(int));
+ }
+
+ Expression body = Expression.Add(left, right);
+
+ // Convert back if needed
+ if (type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort))
+ {
+ body = Expression.Convert(body, type);
+ }
+
+ return Expression.Lambda>(body, paramA, paramB).Compile();
+ });
+
+ return addFunc(a, b);
+ }
+
+ // Helper methods to get typed values without INumber constraint
+ private static T GetOne()
+ {
+ return (T)Convert.ChangeType(1, typeof(T));
+ }
+
+ private static T GetTwo()
+ {
+ return (T)Convert.ChangeType(2, typeof(T));
+ }
+ }
+}
\ No newline at end of file
diff --git a/csharp/Platform.Data.Doublets.Tests/EqualityTests.cs b/csharp/Platform.Data.Doublets.Tests/EqualityTests.cs
new file mode 100644
index 000000000..1238f662c
--- /dev/null
+++ b/csharp/Platform.Data.Doublets.Tests/EqualityTests.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Linq.Expressions;
+using Xunit;
+
+namespace Platform.Data.Doublets.Tests
+{
+ public static class EqualityTests
+ {
+ [Fact]
+ public static void GenericEqualityTest()
+ {
+ // Test IEqualityOperators approach (NET 7+) - recommended approach
+ TestIEqualityOperatorsComparison();
+ TestIEqualityOperatorsComparison();
+ TestIEqualityOperatorsComparison();
+ TestIEqualityOperatorsComparison();
+
+ // Test EqualityComparer approach
+ TestEqualityComparerComparison();
+ TestEqualityComparerComparison();
+ TestEqualityComparerComparison();
+ TestEqualityComparerComparison();
+
+ // Test dynamic approach - for comparison
+ TestDynamicComparison();
+ TestDynamicComparison();
+ TestDynamicComparison();
+ TestDynamicComparison();
+
+ // Test expression-based approach
+ TestExpressionComparison();
+ TestExpressionComparison();
+ TestExpressionComparison();
+ TestExpressionComparison();
+ }
+
+ private static void TestIEqualityOperatorsComparison() where T : IEqualityOperators
+ {
+ var a = GetOne();
+ var b = GetOne();
+ var c = GetTwo();
+
+ Assert.True(IEqualityOperatorsEquals(a, b));
+ Assert.False(IEqualityOperatorsEquals(a, c));
+ }
+
+ private static void TestEqualityComparerComparison()
+ {
+ var a = GetOne();
+ var b = GetOne();
+ var c = GetTwo();
+
+ Assert.True(EqualityComparerEquals(a, b));
+ Assert.False(EqualityComparerEquals(a, c));
+ }
+
+ private static void TestDynamicComparison()
+ {
+ var a = GetOne();
+ var b = GetOne();
+ var c = GetTwo();
+
+ Assert.True(DynamicEquals(a, b));
+ Assert.False(DynamicEquals(a, c));
+ }
+
+ private static void TestExpressionComparison()
+ {
+ var a = GetOne();
+ var b = GetOne();
+ var c = GetTwo();
+
+ Assert.True(ExpressionEquals(a, b));
+ Assert.False(ExpressionEquals(a, c));
+ }
+
+ // IEqualityOperators approach (NET 7+ - best performance)
+ private static bool IEqualityOperatorsEquals(T a, T b) where T : IEqualityOperators
+ {
+ return a == b;
+ }
+
+ // EqualityComparer approach (good performance)
+ private static bool EqualityComparerEquals(T a, T b)
+ {
+ return EqualityComparer.Default.Equals(a, b);
+ }
+
+ // Dynamic approach (slowest)
+ private static bool DynamicEquals(T a, T b)
+ {
+ dynamic dynamicA = a;
+ dynamic dynamicB = b;
+ return (bool)(dynamicA == dynamicB);
+ }
+
+ // Expression-based approach (moderate performance)
+ private static readonly System.Collections.Concurrent.ConcurrentDictionary _equalityFunctions =
+ new System.Collections.Concurrent.ConcurrentDictionary();
+
+ private static bool ExpressionEquals(T a, T b)
+ {
+ var equalityFunc = (Func)_equalityFunctions.GetOrAdd(typeof(T), type =>
+ {
+ var paramA = Expression.Parameter(type, "a");
+ var paramB = Expression.Parameter(type, "b");
+ var body = Expression.Equal(paramA, paramB);
+ return Expression.Lambda>(body, paramA, paramB).Compile();
+ });
+
+ return equalityFunc(a, b);
+ }
+
+ // Helper methods to get typed values
+ private static T GetOne()
+ {
+ return (T)Convert.ChangeType(1, typeof(T));
+ }
+
+ private static T GetTwo()
+ {
+ return (T)Convert.ChangeType(2, typeof(T));
+ }
+ }
+}
\ No newline at end of file