using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
namespace Everest.Library.LINQ{
public static class ReflectionExtensions
{
public static object GetValue(this MemberInfo member, object instance)
{
switch (member.MemberType)
{
case MemberTypes.Property:
return ((PropertyInfo)member).GetValue(instance, null);
case MemberTypes.Field:
return ((FieldInfo)member).GetValue(instance);
default:
throw new InvalidOperationException();
}
}
public static void SetValue(this MemberInfo member, object instance, object value)
{
switch (member.MemberType)
{
case MemberTypes.Property:
var pi = (PropertyInfo)member;
pi.SetValue(instance, value, null);
break;
case MemberTypes.Field:
var fi = (FieldInfo)member;
fi.SetValue(instance, value);
break;
default:
throw new InvalidOperationException();
}
}
}
public static class TypeHelper
{
public static Type FindIEnumerable(Type seqType)
{
if (seqType == null || seqType == typeof(string))
return null;
if (seqType.IsArray)
return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
if (seqType.IsGenericType)
{
foreach (Type arg in seqType.GetGenericArguments())
{
Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
if (ienum.IsAssignableFrom(seqType))
{
return ienum;
}
}
}
Type[] ifaces = seqType.GetInterfaces();
if (ifaces != null && ifaces.Length > 0)
{
foreach (Type iface in ifaces)
{
Type ienum = FindIEnumerable(iface);
if (ienum != null) return ienum;
}
}
if (seqType.BaseType != null && seqType.BaseType != typeof(object))
{
return FindIEnumerable(seqType.BaseType);
}
return null;
}
public static Type GetSequenceType(Type elementType)
{
return typeof(IEnumerable<>).MakeGenericType(elementType);
}
public static Type GetElementType(Type seqType)
{
Type ienum = FindIEnumerable(seqType);
if (ienum == null) return seqType;
return ienum.GetGenericArguments()[0];
}
public static bool IsNullableType(Type type)
{
return type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
public static bool IsNullAssignable(Type type)
{
return !type.IsValueType || IsNullableType(type);
}
public static Type GetNonNullableType(Type type)
{
if (IsNullableType(type))
{
return type.GetGenericArguments()[0];
}
return type;
}
public static Type GetNullAssignableType(Type type)
{
if (!IsNullAssignable(type))
{
return typeof(Nullable<>).MakeGenericType(type);
}
return type;
}
public static ConstantExpression GetNullConstant(Type type)
{
return Expression.Constant(null, GetNullAssignableType(type));
}
public static Type GetMemberType(MemberInfo mi)
{
FieldInfo fi = mi as FieldInfo;
if (fi != null) return fi.FieldType;
PropertyInfo pi = mi as PropertyInfo;
if (pi != null) return pi.PropertyType;
EventInfo ei = mi as EventInfo;
if (ei != null) return ei.EventHandlerType;
MethodInfo meth = mi as MethodInfo; // property getters really
if (meth != null) return meth.ReturnType;
return null;
}
public static object GetDefault(Type type)
{
bool isNullable = !type.IsValueType || TypeHelper.IsNullableType(type);
if (!isNullable)
return Activator.CreateInstance(type);
return null;
}
public static bool IsReadOnly(MemberInfo member)
{
switch (member.MemberType)
{
case MemberTypes.Field:
return (((FieldInfo)member).Attributes & FieldAttributes.InitOnly) != 0;
case MemberTypes.Property:
PropertyInfo pi = (PropertyInfo)member;
return !pi.CanWrite || pi.GetSetMethod() == null;
default:
return true;
}
}
public static bool IsInteger(Type type)
{
Type nnType = GetNonNullableType(type);
switch (Type.GetTypeCode(type))
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
default:
return false;
}
}
}
public static class PartialEvaluator
{
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression Eval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
{
return SubtreeEvaluator.Eval(Nominator.Nominate(fnCanBeEvaluated, expression), expression);
}
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression Eval(Expression expression)
{
return Eval(expression, PartialEvaluator.CanBeEvaluatedLocally);
}
private static bool CanBeEvaluatedLocally(Expression expression)
{
return expression.NodeType != ExpressionType.Parameter;
}
/// <summary>
/// Evaluates & replaces sub-trees when first candidate is reached (top-down)
/// </summary>
class SubtreeEvaluator : ExpressionVisitor
{
HashSet<Expression> candidates;
private SubtreeEvaluator(HashSet<Expression> candidates)
{
this.candidates = candidates;
}
internal static Expression Eval(HashSet<Expression> candidates, Expression exp)
{
return new SubtreeEvaluator(candidates).Visit(exp);
}
protected override Expression Visit(Expression exp)
{
if (exp == null)
{
return null;
}
if (this.candidates.Contains(exp))
{
return this.Evaluate(exp);
}
return base.Visit(exp);
}
private Expression Evaluate(Expression e)
{
Type type = e.Type;
if (e.NodeType == ExpressionType.Convert)
{
// check for unnecessary convert & strip them
var u = (UnaryExpression)e;
if (TypeHelper.GetNonNullableType(u.Operand.Type) == TypeHelper.GetNonNullableType(type))
{
e = ((UnaryExpression)e).Operand;
}
}
if (e.NodeType == ExpressionType.Constant)
{
// in case we actually threw out a nullable conversion above, simulate it here
if (e.Type == type)
{
return e;
}
else if (TypeHelper.GetNonNullableType(e.Type) == TypeHelper.GetNonNullableType(type))
{
return Expression.Constant(((ConstantExpression)e).Value, type);
}
}
var me = e as MemberExpression;
if (me != null)
{
// member accesses off of constant's are common, and yet since these partial evals
// are never re-used, using reflection to access the member is faster than compiling
// and invoking a lambda
var ce = me.Expression as ConstantExpression;
if (ce != null)
{
return Expression.Constant(me.Member.GetValue(ce.Value), type);
}
}
if (type.IsValueType)
{
e = Expression.Convert(e, typeof(object));
}
Expression<Func<object>> lambda = Expression.Lambda<Func<object>>(e);
#if NOREFEMIT
Func<object> fn = ExpressionEvaluator.CreateDelegate(lambda);
#else
Func<object> fn = lambda.Compile();
#endif
return Expression.Constant(fn(), type);
}
}
/// <summary>
/// Performs bottom-up analysis to determine which nodes can possibly
/// be part of an evaluated sub-tree.
/// </summary>
class Nominator : ExpressionVisitor
{
Func<Expression, bool> fnCanBeEvaluated;
HashSet<Expression> candidates;
bool cannotBeEvaluated;
private Nominator(Func<Expression, bool> fnCanBeEvaluated)
{
this.candidates = new HashSet<Expression>();
this.fnCanBeEvaluated = fnCanBeEvaluated;
}
internal static HashSet<Expression> Nominate(Func<Expression, bool> fnCanBeEvaluated, Expression expression)
{
Nominator nominator = new Nominator(fnCanBeEvaluated);
nominator.Visit(expression);
return nominator.candidates;
}
protected override Expression VisitConstant(ConstantExpression c)
{
return base.VisitConstant(c);
}
protected override Expression Visit(Expression expression)
{
if (expression != null)
{
bool saveCannotBeEvaluated = this.cannotBeEvaluated;
this.cannotBeEvaluated = false;
base.Visit(expression);
if (!this.cannotBeEvaluated)
{
if (this.fnCanBeEvaluated(expression))
{
this.candidates.Add(expression);
}
else
{
this.cannotBeEvaluated = true;
}
}
this.cannotBeEvaluated |= saveCannotBeEvaluated;
}
return expression;
}
}
}
}
|