Skip to content

Instantly share code, notes, and snippets.

@kemsky
Last active April 1, 2023 05:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kemsky/77c44d461356c41ff76725a53b722a35 to your computer and use it in GitHub Desktop.
Save kemsky/77c44d461356c41ff76725a53b722a35 to your computer and use it in GitHub Desktop.
Remaining bits
public static class ExpressionExtensions
{
public static Option<object, Expression> TryEvaluate(this Expression expression)
{
if (expression is MemberExpression memberExpression)
{
if (memberExpression.Expression == null)
{
if (memberExpression.Member is PropertyInfo staticProperty)
{
return Option.Some<object, Expression>(staticProperty.GetValue(null));
}
else if (memberExpression.Member is FieldInfo staticField)
{
return Option.Some<object, Expression>(staticField.GetValue(null));
}
else
{
throw new Exception($"Not supported: {memberExpression}");
}
}
var stack = new Stack<MemberExpression>();
var e = memberExpression;
while (e != null)
{
stack.Push(e);
e = e.Expression as MemberExpression;
}
if (stack.Peek().Expression is ConstantExpression constantExpression)
{
var value = constantExpression.Value;
while (stack.TryPop(out var stackExpression))
{
if (stackExpression.Member is PropertyInfo propertyInfo)
{
value = propertyInfo.GetValue(value);
}
else if (stackExpression.Member is FieldInfo fieldInfo)
{
value = fieldInfo.GetValue(value);
}
else
{
return Option.None<object, Expression>(expression);
}
}
return Option.Some<object, Expression>(value);
}
else
{
return Option.None<object, Expression>(expression);
}
}
else if (expression is ConstantExpression constantExpression)
{
return Option.Some<object, Expression>(constantExpression.Value);
}
return Option.None<object, Expression>(expression);
}
}
public class RebindParameter : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly Expression _replace;
public RebindParameter(ParameterExpression parameter, Expression replace)
{
_parameter = parameter;
_replace = replace;
}
public override Expression Visit(Expression node)
{
if (node is ParameterExpression parameterExpression && parameterExpression == _parameter)
{
return _replace;
}
else
{
return base.Visit(node);
}
}
}
public class ProjectionSingleVisitor : ExpressionVisitor
{
protected override Expression VisitInvocation(InvocationExpression node)
{
if (node.Expression is MemberExpression memberExpression)
{
if (memberExpression.Member.DeclaringType?.IsGenericType == true && memberExpression.Member.DeclaringType.GetGenericTypeDefinition() == typeof(Projection<,>))
{
var projection = (IProjection)memberExpression.Expression.TryEvaluate().ValueOrFailure();
var lambda = projection.GetProjectToExpression();
var body = lambda.Body;
var rebindParameter = new RebindParameter(lambda.Parameters[0], node.Arguments[0]);
var expression = rebindParameter.Visit(body);
if (expression.Type.IsClass || Nullable.GetUnderlyingType(expression.Type) != null)
{
var checkNullExpression = Expression.Equal(node.Arguments[0], Expression.Constant(null, node.Arguments[0].Type));
var nullExpression = Expression.Constant(null, expression.Type);
var conditional = Expression.Condition(checkNullExpression, nullExpression, expression);
return base.Visit(conditional);
}
else
{
return base.Visit(expression);
}
}
}
return base.VisitInvocation(node);
}
}
@lejafo
Copy link

lejafo commented Mar 31, 2023

Hi!
Please, can you tell me what is the type Option<,>? It is from some functional lib for c#?

@kemsky
Copy link
Author

kemsky commented Mar 31, 2023

@lejafo, its from Optional package: https://github.com/nlkl/Optional

@lejafo
Copy link

lejafo commented Apr 1, 2023

Thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment