diff --git a/UnitsNet.Tests/CompiledLambdasTests.cs b/UnitsNet.Tests/CompiledLambdasTests.cs new file mode 100644 index 0000000000..5ef295d2b6 --- /dev/null +++ b/UnitsNet.Tests/CompiledLambdasTests.cs @@ -0,0 +1,95 @@ +using System; +using Xunit; + +namespace UnitsNet.Tests +{ + public class CompiledLambdasTests + { + [Theory] + [InlineData(3.0, 2.0, 6.0)] + public void MultiplyReturnsExpectedValues(TLeft left, TRight right, TResult expected) + { + Assert.Equal(expected, CompiledLambdas.Multiply(left, right)); + } + + [Theory] + [InlineData(6.0, 2.0, 3.0)] + public void DivideReturnsExpectedValues(TLeft left, TRight right, TResult expected) + { + Assert.Equal(expected, CompiledLambdas.Divide(left, right)); + } + + [Theory] + [InlineData(3.0, 2.0, 5.0)] + public void AddReturnsExpectedValues(TLeft left, TRight right, TResult expected) + { + Assert.Equal(expected, CompiledLambdas.Add(left, right)); + } + + [Theory] + [InlineData(3.0, 2.0, 1.0)] + public void SubtractReturnsExpectedValues(TLeft left, TRight right, TResult expected) + { + Assert.Equal(expected, CompiledLambdas.Subtract(left, right)); + } + + [Theory] + [InlineData(3.0, 2.0, 1.0)] + public void ModuloReturnsExpectedValues(TLeft left, TRight right, TResult expected) + { + Assert.Equal(expected, CompiledLambdas.Modulo(left, right)); + } + + [Theory] + [InlineData(3.0, 3.0, true)] + [InlineData(3.0, 2.0, false)] + public void EqualReturnsExpectedValues(TLeft left, TRight right, bool expected) + { + Assert.Equal(expected, CompiledLambdas.Equal(left, right)); + } + + [Theory] + [InlineData(3.0, 3.0, false)] + [InlineData(3.0, 2.0, true)] + public void NotEqualReturnsExpectedValues(TLeft left, TRight right, bool expected) + { + Assert.Equal(expected, CompiledLambdas.NotEqual(left, right)); + } + + [Theory] + [InlineData(2.0, 3.0, true)] + [InlineData(2.0, 2.0, false)] + [InlineData(3.0, 2.0, false)] + public void LessThanReturnsExpectedValues(TLeft left, TRight right, bool expected) + { + Assert.Equal(expected, CompiledLambdas.LessThan(left, right)); + } + + [Theory] + [InlineData(2.0, 3.0, true)] + [InlineData(2.0, 2.0, true)] + [InlineData(3.0, 2.0, false)] + public void LessThanOrEqualReturnsExpectedValues(TLeft left, TRight right, bool expected) + { + Assert.Equal(expected, CompiledLambdas.LessThanOrEqual(left, right)); + } + + [Theory] + [InlineData(3.0, 2.0, true)] + [InlineData(3.0, 3.0, false)] + [InlineData(3.0, 4.0, false)] + public void GreaterThanReturnsExpectedValues(TLeft left, TRight right, bool expected) + { + Assert.Equal(expected, CompiledLambdas.GreaterThan(left, right)); + } + + [Theory] + [InlineData(3.0, 2.0, true)] + [InlineData(3.0, 3.0, true)] + [InlineData(3.0, 4.0, false)] + public void GreaterThanOrEqualReturnsExpectedValues(TLeft left, TRight right, bool expected) + { + Assert.Equal(expected, CompiledLambdas.GreaterThanOrEqual(left, right)); + } + } +} diff --git a/UnitsNet/CompiledLambdas.cs b/UnitsNet/CompiledLambdas.cs new file mode 100644 index 0000000000..56790cadaa --- /dev/null +++ b/UnitsNet/CompiledLambdas.cs @@ -0,0 +1,312 @@ +using System; +using System.Linq.Expressions; + +namespace UnitsNet +{ + /// + /// Compiled lambda expressions that can be invoked with generic run-time parameters. This is used for performance as + /// it is far faster than reflection based alternatives. + /// + internal static class CompiledLambdas + { + /// + /// Multiplies the given values. + /// + /// The type of the operation (left hand side, right hand side, and result). + /// The left hand side parameter. + /// The right hand side parameter. + /// The multiplied result. + internal static T Multiply(T left, T right) => MultiplyImplementation.Invoke(left, right); + + /// + /// Multiplies the given values. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The result type. + /// The left hand side parameter. + /// The right hand side parameter. + /// The multiplied result. + internal static TResult Multiply(TLeft left, TRight right) => + MultiplyImplementation.Invoke(left, right); + + /// + /// Divides the given values. + /// + /// The type of the operation (left hand side, right hand side, and result). + /// The left hand side parameter. + /// The right hand side parameter. + /// The divided result. + internal static T Divide(T left, T right) => DivideImplementation.Invoke(left, right); + + /// + /// Divides the given values. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The result type. + /// The left hand side parameter. + /// The right hand side parameter. + /// The divided result. + internal static TResult Divide(TLeft left, TRight right) => + DivideImplementation.Invoke(left, right); + + /// + /// Adds the given values. + /// + /// The type of the operation (left hand side, right hand side, and result). + /// The left hand side parameter. + /// The right hand side parameter. + /// The added result. + internal static T Add(T left, T right) => AddImplementation.Invoke(left, right); + + /// + /// Adds the given values. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The result type. + /// The left hand side parameter. + /// The right hand side parameter. + /// The added result. + internal static TResult Add(TLeft left, TRight right) => + AddImplementation.Invoke(left, right); + + /// + /// Subtracts the given values. + /// + /// The type of the operation (left hand side, right hand side, and result). + /// The left hand side parameter. + /// The right hand side parameter. + /// The subtracted result. + internal static T Subtract(T left, T right) => SubtractImplementation.Invoke(left, right); + + /// + /// Subtracts the given values. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The result type. + /// The left hand side parameter. + /// The right hand side parameter. + /// The subtracted result. + internal static TResult Subtract(TLeft left, TRight right) => + SubtractImplementation.Invoke(left, right); + + /// + /// Gets the modulus of the given values. + /// + /// The type of the operation (left hand side, right hand side, and result). + /// The left hand side parameter. + /// The right hand side parameter. + /// The modulus. + internal static T Modulo(T left, T right) => ModuloImplementation.Invoke(left, right); + + /// + /// Gets the modulus of the given values. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The result type. + /// The left hand side parameter. + /// The right hand side parameter. + /// The modulus. + internal static TResult Modulo(TLeft left, TRight right) => + ModuloImplementation.Invoke(left, right); + + /// + /// Checks if the left and right hand side are equal. + /// + /// The type of both the left and right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if equal, otherwise false. + internal static bool Equal(T left, T right) => EqualImplementation.Invoke(left, right); + + /// + /// Checks if the left and right hand side are equal. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if equal, otherwise false. + internal static bool Equal(TLeft left, TRight right) => + EqualImplementation.Invoke(left, right); + + /// + /// Checks if the left and right hand side are not equal. + /// + /// The type of both the left and right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if not equal, otherwise false. + internal static bool NotEqual(T left, T right) => NotEqualImplementation.Invoke(left, right); + + /// + /// Checks if the left and right hand side are not equal. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if not equal, otherwise false. + internal static bool NotEqual(TLeft left, TRight right) => + NotEqualImplementation.Invoke(left, right); + + /// + /// Checks if the left hand side is less than the right hand side. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if the left hand side is less than the right hand side, otherwise false. + internal static bool LessThan(TLeft left, TRight right) => + LessThanImplementation.Invoke(left, right); + + /// + /// Checks if the left hand side is less than or equal to the right hand side. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if the left hand side is less than or equal to the right hand side, otherwise false. + internal static bool LessThanOrEqual(TLeft left, TRight right) => + LessThanOrEqualImplementation.Invoke(left, right); + + /// + /// Checks if the left hand side is greater than the right hand side. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if the left hand side is greater than the right hand side, otherwise false. + internal static bool GreaterThan(TLeft left, TRight right) => + GreaterThanImplementation.Invoke(left, right); + + /// + /// Checks if the left hand side is greater than or equal to the right hand side. + /// + /// The type of the left hand side. + /// The type of the right hand side. + /// The left hand side parameter. + /// The right hand side parameter. + /// True if the left hand side is greater than or equal to the right hand side, otherwise false. + internal static bool GreaterThanOrEqual(TLeft left, TRight right) => + GreaterThanOrEqualImplementation.Invoke(left, right); + + #region Implementation Classes + + private static class MultiplyImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.Multiply); + + internal static TResult Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class DivideImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.Divide); + + internal static TResult Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class AddImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.Add); + + internal static TResult Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class SubtractImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.Subtract); + + internal static TResult Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class ModuloImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.Modulo); + + internal static TResult Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class EqualImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.Equal); + + internal static bool Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class NotEqualImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.NotEqual); + + internal static bool Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class LessThanImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.LessThan); + + internal static bool Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class LessThanOrEqualImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.LessThanOrEqual); + + internal static bool Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class GreaterThanImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.GreaterThan); + + internal static bool Invoke(TLeft left, TRight right) => Function(left, right); + } + + private static class GreaterThanOrEqualImplementation + { + private readonly static Func Function = + CreateBinaryFunction(Expression.GreaterThanOrEqual); + + internal static bool Invoke(TLeft left, TRight right) => Function(left, right); + } + + #endregion + + /// + /// Creates a compiled lambda for the given . + /// + /// The type of the left hand side of the binary operation. + /// The type of the right hand side of the binary operation. + /// The type of the result of the binary operation. + /// The function that creates a binary expression to compile. + /// The compiled binary expression. + private static Func CreateBinaryFunction(Func expressionCreationFunction) + { + var leftParameter = Expression.Parameter(typeof(TLeft), "left"); + var rightParameter = Expression.Parameter(typeof(TRight), "right"); + + var binaryExpression = expressionCreationFunction(leftParameter, rightParameter); + var lambda = Expression.Lambda>(binaryExpression, leftParameter, rightParameter); + + return lambda.Compile(); + } + } +}