Skip to content

Instantly share code, notes, and snippets.

@dfch
Created March 5, 2017 19:18
Show Gist options
  • Save dfch/951d355ad929fba966aec7dccd67d199 to your computer and use it in GitHub Desktop.
Save dfch/951d355ad929fba966aec7dccd67d199 to your computer and use it in GitHub Desktop.
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace Net.Appclusive.Public.Types
{
public abstract class Boxed : IConvertible
{
public virtual TypeCode GetTypeCode()
{
return TypeCode.Object;
}
public virtual bool ToBoolean(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual char ToChar(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual sbyte ToSByte(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual byte ToByte(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual short ToInt16(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual ushort ToUInt16(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual int ToInt32(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual uint ToUInt32(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual long ToInt64(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual ulong ToUInt64(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual float ToSingle(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual double ToDouble(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual decimal ToDecimal(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual DateTime ToDateTime(IFormatProvider provider)
{
throw new NotImplementedException();
}
public virtual string ToString(IFormatProvider provider)
{
return null != provider
? string.Format(provider, base.ToString())
: base.ToString();
}
public abstract object ToType(Type conversionType, IFormatProvider provider);
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using BoxedType=System.Linq.Expressions.LambdaExpression;
namespace Net.Appclusive.Public.Types
{
public sealed class BoxedLambdaExpression : Boxed<BoxedType>
{
private const int PARAMETER_COUNT = 1;
private const int PARAMETER_INDEX = 0;
private const string PARAMETER_NAME = "$it";
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static BoxedType Combine(BoxedLambdaExpression expression1, BoxedType expression2, bool isOrElse)
{
Contract.Assert(PARAMETER_COUNT == expression1?.Value.Parameters.Count);
Contract.Assert(PARAMETER_COUNT == expression2?.Parameters.Count);
Contract.Assert(expression1.Value.Parameters[PARAMETER_INDEX].Type == expression2.Parameters[PARAMETER_INDEX].Type);
var combinedExpression = isOrElse
? Expression.OrElse(expression1.Value.Body, expression2.Body)
: Expression.AndAlso(expression1.Value.Body, expression2.Body);
var visitor = new MemberExpressionVisitor(Expression.Parameter(expression1.Value.Parameters[PARAMETER_INDEX].Type, PARAMETER_NAME));
var replacedExpression = visitor.Visit(combinedExpression);
var lambdaType = typeof(Func<,>).MakeGenericType(visitor.Parameter.Type, expression1.Value.ReturnType);
Contract.Assert(null != lambdaType);
var lambdaExpression = Expression.Lambda(lambdaType, replacedExpression, visitor.Parameter);
return lambdaExpression;
}
public static implicit operator BoxedType(BoxedLambdaExpression lambdaExpression)
{
return lambdaExpression.Value;
}
public static implicit operator BoxedLambdaExpression(BoxedType boxedExpression)
{
return new BoxedLambdaExpression
{
Value = boxedExpression
};
}
public static BoxedType operator +(BoxedLambdaExpression expression1, BoxedType expression2)
{
return Combine(expression1, expression2, false);
}
public static BoxedType operator &(BoxedLambdaExpression expression1, BoxedType expression2)
{
return Combine(expression1, expression2, false);
}
public static BoxedType operator |(BoxedLambdaExpression expression1, BoxedType expression2)
{
return Combine(expression1, expression2, true);
}
public override object ToType(Type conversionType, IFormatProvider provider)
{
// DFTODO - determine if and how we should implement a type conversion
throw new NotImplementedException();
}
private class MemberExpressionVisitor : ExpressionVisitor
{
public readonly ParameterExpression Parameter;
public MemberExpressionVisitor(ParameterExpression parameter)
{
Contract.Requires(null != parameter);
Parameter = parameter;
}
protected override Expression VisitMember(MemberExpression node)
{
return Expression.Property
(
Parameter,
node.Member.Name
);
}
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq.Expressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Net.Appclusive.Public.Types;
namespace Net.Appclusive.Public.Tests.Types
{
[TestClass]
public class BoxedLambdaExpressionTest
{
public class SomeObject
{
public long Id { get; set; }
public string Name { get; set; }
}
// ReSharper disable once InconsistentNaming
private static readonly Expression<Func<SomeObject, bool>> VALUE = e => e.Id != 0;
[TestMethod]
public void BoxingSucceeds()
{
BoxedLambdaExpression boxedLambda = VALUE;
Assert.AreEqual(VALUE, boxedLambda.Value);
}
[TestMethod]
public void UnboxingSucceeds()
{
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression unboxed = boxed;
Assert.AreEqual(VALUE, unboxed);
}
[TestMethod]
public void ImplicitCast1Succeeds()
{
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression expression = boxed;
Assert.AreEqual(VALUE, expression);
}
[TestMethod]
public void ImplicitCast2Succeeds()
{
var boxed = VALUE;
Expression expression = boxed;
Assert.AreEqual(VALUE, expression);
}
[TestMethod]
public void OperatorAddSucceeds()
{
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression<Func<SomeObject, bool>> expression = e => !string.IsNullOrWhiteSpace(e.Name);
var result = boxed + expression;
var someObject = new SomeObject
{
Id = 42,
Name = "tralala"
};
var value = result.Compile().DynamicInvoke(someObject);
Assert.AreEqual(true, value);
}
[TestMethod]
public void OperatorAndSucceeds()
{
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression<Func<SomeObject, bool>> expression = e => !string.IsNullOrWhiteSpace(e.Name);
var result = boxed & expression;
var someObject = new SomeObject
{
Id = 0,
Name = "tralala"
};
var value = result.Compile().DynamicInvoke(someObject);
Assert.AreEqual(false, value);
}
[TestMethod]
public void OperatorOrSucceeds()
{
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression<Func<SomeObject, bool>> expression = e => !string.IsNullOrWhiteSpace(e.Name);
var result = boxed | expression;
var someObject = new SomeObject
{
Id = 0,
Name = string.Empty
};
var value = result.Compile().DynamicInvoke(someObject);
Assert.AreEqual(false, value);
}
[TestMethod]
public void ToString1Succeeds()
{
var expected = @"$it => (($it.Id != 0) AndAlso Not(IsNullOrWhiteSpace($it.Name)))";
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression<Func<SomeObject, bool>> expression = e => !string.IsNullOrWhiteSpace(e.Name);
var result = boxed + expression;
var value = result.ToString();
Assert.AreEqual(expected, value);
}
[TestMethod]
public void ToString2Succeeds()
{
var expected = @"$it => (($it.Id != 0) AndAlso Not(IsNullOrWhiteSpace($it.Name)))";
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression<Func<SomeObject, bool>> expression = e => !string.IsNullOrWhiteSpace(e.Name);
var result = boxed & expression;
var value = result.ToString();
Assert.AreEqual(expected, value);
}
[TestMethod]
public void ToString3Succeeds()
{
var expected = @"$it => (($it.Id != 0) OrElse Not(IsNullOrWhiteSpace($it.Name)))";
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
Expression<Func<SomeObject, bool>> expression = e => !string.IsNullOrWhiteSpace(e.Name);
var result = boxed | expression;
var value = result.ToString();
Assert.AreEqual(expected, value);
}
[TestMethod]
[ExpectedException(typeof(NotImplementedException))]
public void ChangeTypeSucceeds()
{
var boxed = new BoxedLambdaExpression
{
Value = VALUE
};
object valueAsObject = Convert.ChangeType(boxed, typeof(bool));
}
}
}
/**
* Copyright 2017 d-fens GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Net.Appclusive.Public.Types
{
public abstract class Boxed<T> : Boxed
{
public T Value { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment