问题
I need to create a function to evaluate queries for some rules before executing them. Here's the code:
public class DataInfo
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
static class Program
{
static void Main()
{
var data = new DataInfo()
{
A = 10,
B = 5,
C = -1
};
// the result should be -1
int result = Calcul<DataInfo>(data, x => x.A / x.B + x.C);
}
static int Calcul<T>(T data, Expression<Func<T, int>> query)
{
// PSEUDO CODE
// if one property used in the query have a
// value of -1 or -2 then return 0
// {
// return 0;
// }
// if one property used in the query have a
// value of 0 AND it is used on the right side of
// a Divide operation then return -1
// {
// return -1;
// }
// if the query respect the rules, apply the query and return the value
return query.Compile().Invoke(data);
}
}
In the previous code, the calcul want to divide A(10) with B(5) and then add C(-1). The rules said that if one property used in the query have a value of -1 or -2, return 0. So in this example, the value return should be -1. If the query respect the rules, then apply the query on the data and return the value.
So how can i extract the properties used in the query and test the value used in them before appying the query on the data?
回答1:
You need to use an ExpressionVisitor to test the property values. Here is an example of how you could implement the logic.
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
// HasDivideByZero - the result should be -1
int result1 = Calcul<DataInfo>(new DataInfo { A = 10, B = 0, C = 1 }, x => x.A / x.B + x.C);
// HasNegative - the result should be 0
int result2 = Calcul<DataInfo>(new DataInfo { A = 10, B = 5, C = -1 }, x => x.A / x.B + x.C);
// the result should be 3
int result3 = Calcul<DataInfo>(new DataInfo { A = 10, B = 5, C = 1 }, x => x.A / x.B + x.C);
}
static int Calcul<T>(T data, Expression<Func<T, int>> query)
{
if (NegativeValueChecker<T>.HasNegative(data, query))
{
return 0;
}
if (DivideByZeroChecker<T>.HasDivideByZero(data, query))
{
return -1;
}
return query.Compile().Invoke(data);
}
}
class DivideByZeroChecker<T> : ExpressionVisitor
{
private readonly T _data;
private bool _hasDivideByZero;
public static bool HasDivideByZero(T data, Expression expression)
{
var visitor = new DivideByZeroChecker<T>(data);
visitor.Visit(expression);
return visitor._hasDivideByZero;
}
public DivideByZeroChecker(T data)
{
this._data = data;
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (!this._hasDivideByZero && node.NodeType == ExpressionType.Divide)
{
var rightMemeberExpression = (MemberExpression)node.Right;
var propertyInfo = (PropertyInfo)rightMemeberExpression.Member;
var value = Convert.ToInt32(propertyInfo.GetValue(this._data, null));
this._hasDivideByZero = value == 0;
}
return base.VisitBinary(node);
}
}
class NegativeValueChecker<T> : ExpressionVisitor
{
private readonly T _data;
public bool _hasNegative;
public static bool HasNegative(T data, Expression expression)
{
var visitor = new NegativeValueChecker<T>(data);
visitor.Visit(expression);
return visitor._hasNegative;
}
public NegativeValueChecker(T data)
{
this._data = data;
}
protected override Expression VisitMember(MemberExpression node)
{
if (!this._hasNegative)
{
var propertyInfo = (PropertyInfo)node.Member;
var value = Convert.ToInt32(propertyInfo.GetValue(this._data, null));
this._hasNegative = value < 0;
}
return base.VisitMember(node);
}
}
class DataInfo
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
}
回答2:
Get a look at the source of Moq - http://code.google.com/p/moq/.
来源:https://stackoverflow.com/questions/8508316/how-to-extract-properties-used-in-a-expressionfunct-tresult-query-and-test