2012-07-26 21:12:54 +06:00
using System ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Linq ;
using System.Linq.Expressions ;
using System.Reflection ;
namespace Umbraco.Core
{
/// <summary>
/// A set of helper methods for dealing with expressions
/// </summary>
/// <remarks></remarks>
internal static class ExpressionHelper
{
private static readonly ConcurrentDictionary < LambdaExpressionCacheKey , PropertyInfo > PropertyInfoCache = new ConcurrentDictionary < LambdaExpressionCacheKey , PropertyInfo >();
/// <summary>
/// Gets a <see cref="PropertyInfo"/> object from an expression.
/// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="source">The source.</param>
/// <param name="propertyLambda">The property lambda.</param>
/// <returns></returns>
/// <remarks></remarks>
public static PropertyInfo GetPropertyInfo < TSource , TProperty >( this TSource source , Expression < Func < TSource , TProperty >> propertyLambda )
{
return GetPropertyInfo ( propertyLambda );
}
/// <summary>
/// Gets a <see cref="PropertyInfo"/> object from an expression.
/// </summary>
/// <typeparam name="TSource">The type of the source.</typeparam>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="propertyLambda">The property lambda.</param>
/// <returns></returns>
/// <remarks></remarks>
public static PropertyInfo GetPropertyInfo < TSource , TProperty >( Expression < Func < TSource , TProperty >> propertyLambda )
{
return PropertyInfoCache . GetOrAdd (
new LambdaExpressionCacheKey ( propertyLambda ),
x =>
{
var type = typeof ( TSource );
var member = propertyLambda . Body as MemberExpression ;
if ( member == null )
{
if ( propertyLambda . Body . GetType (). Name == "UnaryExpression" )
{
// The expression might be for some boxing, e.g. representing a value type like HiveId as an object
// in which case the expression will be Convert(x.MyProperty)
var unary = propertyLambda . Body as UnaryExpression ;
if ( unary != null )
{
var boxedMember = unary . Operand as MemberExpression ;
if ( boxedMember == null )
throw new ArgumentException ( "The type of property could not be infered, try specifying the type parameters explicitly. This can happen if you have tried to access PropertyInfo where the property's return type is a value type, but the expression is trying to convert it to an object" );
else member = boxedMember ;
}
}
else throw new ArgumentException ( string . Format ( "Expression '{0}' refers to a method, not a property." , propertyLambda ));
}
var propInfo = member . Member as PropertyInfo ;
if ( propInfo == null )
throw new ArgumentException ( string . Format (
"Expression '{0}' refers to a field, not a property." ,
propertyLambda ));
if ( type != propInfo . ReflectedType &&
! type . IsSubclassOf ( propInfo . ReflectedType ))
throw new ArgumentException ( string . Format (
"Expresion '{0}' refers to a property that is not from type {1}." ,
propertyLambda ,
type ));
return propInfo ;
});
}
2012-10-23 11:29:16 -02:00
public static MemberInfo FindProperty ( LambdaExpression lambdaExpression )
{
Expression expressionToCheck = lambdaExpression ;
bool done = false ;
while (! done )
{
switch ( expressionToCheck . NodeType )
{
case ExpressionType . Convert :
expressionToCheck = (( UnaryExpression ) expressionToCheck ). Operand ;
break ;
case ExpressionType . Lambda :
expressionToCheck = (( LambdaExpression ) expressionToCheck ). Body ;
break ;
case ExpressionType . MemberAccess :
var memberExpression = (( MemberExpression ) expressionToCheck );
if ( memberExpression . Expression . NodeType != ExpressionType . Parameter &&
memberExpression . Expression . NodeType != ExpressionType . Convert )
{
throw new ArgumentException ( string . Format ( "Expression '{0}' must resolve to top-level member and not any child object's properties. Use a custom resolver on the child type or the AfterMap option instead." , lambdaExpression ), "lambdaExpression" );
}
MemberInfo member = memberExpression . Member ;
return member ;
default :
done = true ;
break ;
}
}
throw new Exception ( "Configuration for members is only supported for top-level individual members on a type." );
}
2012-07-26 21:12:54 +06:00
public static IDictionary < string , object > GetMethodParams < T1 , T2 >( Expression < Func < T1 , T2 >> fromExpression )
{
if ( fromExpression == null ) return null ;
var body = fromExpression . Body as MethodCallExpression ;
if ( body == null )
return new Dictionary < string , object >();
var rVal = new Dictionary < string , object >();
var parameters = body . Method . GetParameters (). Select ( x => x . Name ). ToArray ();
var i = 0 ;
foreach ( var argument in body . Arguments )
{
var lambda = Expression . Lambda ( argument , fromExpression . Parameters );
var d = lambda . Compile ();
var value = d . DynamicInvoke ( new object [ 1 ]);
rVal . Add ( parameters [ i ], value );
i ++;
}
return rVal ;
}
/// <summary>
/// Gets a <see cref="MethodInfo"/> from an <see cref="Expression{Action{T}}"/> provided it refers to a method call.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fromExpression">From expression.</param>
/// <returns>The <see cref="MethodInfo"/> or null if <paramref name="fromExpression"/> is null or cannot be converted to <see cref="MethodCallExpression"/>.</returns>
/// <remarks></remarks>
public static MethodInfo GetMethodInfo < T >( Expression < Action < T >> fromExpression )
{
if ( fromExpression == null ) return null ;
var body = fromExpression . Body as MethodCallExpression ;
return body != null ? body . Method : null ;
}
/// <summary>
/// Gets the method info.
/// </summary>
/// <typeparam name="TReturn">The return type of the method.</typeparam>
/// <param name="fromExpression">From expression.</param>
/// <returns></returns>
public static MethodInfo GetMethodInfo < TReturn >( Expression < Func < TReturn >> fromExpression )
{
if ( fromExpression == null ) return null ;
var body = fromExpression . Body as MethodCallExpression ;
return body != null ? body . Method : null ;
}
/// <summary>
/// Gets the method info.
/// </summary>
/// <typeparam name="T1">The type of the 1.</typeparam>
/// <typeparam name="T2">The type of the 2.</typeparam>
/// <param name="fromExpression">From expression.</param>
/// <returns></returns>
public static MethodInfo GetMethodInfo < T1 , T2 >( Expression < Func < T1 , T2 >> fromExpression )
{
if ( fromExpression == null ) return null ;
2013-10-16 11:31:35 +11:00
MethodCallExpression me ;
switch ( fromExpression . Body . NodeType )
{
case ExpressionType . Convert :
case ExpressionType . ConvertChecked :
var ue = fromExpression . Body as UnaryExpression ;
me = (( ue != null ) ? ue . Operand : null ) as MethodCallExpression ;
break ;
default :
me = fromExpression . Body as MethodCallExpression ;
break ;
}
return me != null ? me . Method : null ;
2012-07-26 21:12:54 +06:00
}
/// <summary>
/// Gets a <see cref="MethodInfo"/> from an <see cref="Expression"/> provided it refers to a method call.
/// </summary>
/// <param name="expression">The expression.</param>
/// <returns>The <see cref="MethodInfo"/> or null if <paramref name="expression"/> cannot be converted to <see cref="MethodCallExpression"/>.</returns>
/// <remarks></remarks>
public static MethodInfo GetMethod ( Expression expression )
{
if ( expression == null ) return null ;
return IsMethod ( expression ) ? ((( MethodCallExpression ) expression ). Method ) : null ;
}
/// <summary>
/// Gets a <see cref="MemberInfo"/> from an <see cref="Expression{Func{T, TReturn}}"/> provided it refers to member access.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TReturn">The type of the return.</typeparam>
/// <param name="fromExpression">From expression.</param>
/// <returns>The <see cref="MemberInfo"/> or null if <paramref name="fromExpression"/> cannot be converted to <see cref="MemberExpression"/>.</returns>
/// <remarks></remarks>
public static MemberInfo GetMemberInfo < T , TReturn >( Expression < Func < T , TReturn >> fromExpression )
{
if ( fromExpression == null ) return null ;
var body = fromExpression . Body as MemberExpression ;
return body != null ? body . Member : null ;
}
/// <summary>
/// Determines whether the MethodInfo is the same based on signature, not based on the equality operator or HashCode.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>
/// <c>true</c> if [is method signature equal to] [the specified left]; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// This is useful for comparing Expression methods that may contain different generic types
/// </remarks>
public static bool IsMethodSignatureEqualTo ( this MethodInfo left , MethodInfo right )
{
if ( left . Equals ( right ))
return true ;
if ( left . DeclaringType != right . DeclaringType )
return false ;
if ( left . Name != right . Name )
return false ;
var leftParams = left . GetParameters ();
var rightParams = right . GetParameters ();
if ( leftParams . Length != rightParams . Length )
return false ;
for ( int i = 0 ; i < leftParams . Length ; i ++)
{
//if they are delegate parameters, then assume they match as they could be anything
if ( typeof ( Delegate ). IsAssignableFrom ( leftParams [ i ]. ParameterType ) && typeof ( Delegate ). IsAssignableFrom ( rightParams [ i ]. ParameterType ))
continue ;
//if they are not delegates, then compare the types
if ( leftParams [ i ]. ParameterType != rightParams [ i ]. ParameterType )
return false ;
}
if ( left . ReturnType != right . ReturnType )
return false ;
return true ;
}
/// <summary>
/// Gets a <see cref="MemberInfo"/> from an <see cref="Expression"/> provided it refers to member access.
/// </summary>
/// <param name="expression">The expression.</param>
/// <returns></returns>
/// <remarks></remarks>
public static MemberInfo GetMember ( Expression expression )
{
if ( expression == null ) return null ;
return IsMember ( expression ) ? ((( MemberExpression ) expression ). Member ) : null ;
}
/// <summary>
/// Gets a <see cref="MethodInfo"/> from a <see cref="Delegate"/>
/// </summary>
/// <param name="fromMethodGroup">From method group.</param>
/// <returns></returns>
/// <remarks></remarks>
public static MethodInfo GetStaticMethodInfo ( Delegate fromMethodGroup )
{
if ( fromMethodGroup == null ) throw new ArgumentNullException ( "fromMethodGroup" );
return fromMethodGroup . Method ;
}
///// <summary>
///// Formats an unhandled item for representing the expression as a string.
///// </summary>
///// <typeparam name="T"></typeparam>
///// <param name="unhandledItem">The unhandled item.</param>
///// <returns></returns>
///// <remarks></remarks>
//public static string FormatUnhandledItem<T>(T unhandledItem) where T : class
//{
// if (unhandledItem == null) throw new ArgumentNullException("unhandledItem");
// var itemAsExpression = unhandledItem as Expression;
// return itemAsExpression != null
// ? FormattingExpressionTreeVisitor.Format(itemAsExpression)
// : unhandledItem.ToString();
//}
/// <summary>
/// Determines whether the specified expression is a method.
/// </summary>
/// <param name="expression">The expression.</param>
/// <returns><c>true</c> if the specified expression is method; otherwise, <c>false</c>.</returns>
/// <remarks></remarks>
public static bool IsMethod ( Expression expression )
{
return expression != null && typeof ( MethodCallExpression ). IsAssignableFrom ( expression . GetType ());
}
/// <summary>
/// Determines whether the specified expression is a member.
/// </summary>
/// <param name="expression">The expression.</param>
/// <returns><c>true</c> if the specified expression is member; otherwise, <c>false</c>.</returns>
/// <remarks></remarks>
public static bool IsMember ( Expression expression )
{
return expression != null && typeof ( MemberExpression ). IsAssignableFrom ( expression . GetType ());
}
/// <summary>
/// Determines whether the specified expression is a constant.
/// </summary>
/// <param name="expression">The expression.</param>
/// <returns><c>true</c> if the specified expression is constant; otherwise, <c>false</c>.</returns>
/// <remarks></remarks>
public static bool IsConstant ( Expression expression )
{
return expression != null && typeof ( ConstantExpression ). IsAssignableFrom ( expression . GetType ());
}
/// <summary>
/// Gets the first value from the supplied arguments of an expression, for those arguments that can be cast to <see cref="ConstantExpression"/>.
/// </summary>
/// <param name="arguments">The arguments.</param>
/// <returns></returns>
/// <remarks></remarks>
public static object GetFirstValueFromArguments ( IEnumerable < Expression > arguments )
{
if ( arguments == null ) return false ;
return
arguments . Where ( x => typeof ( ConstantExpression ). IsAssignableFrom ( x . GetType ())). Cast
< ConstantExpression >(). Select ( x => x . Value ). DefaultIfEmpty ( null ). FirstOrDefault ();
}
}
}