tools/AutoMapper/Internal/ReflectionHelper.cs (157 lines of code) (raw):

using System; using System.Collections; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace AutoMapper.Internal { using Configuration; public static class ReflectionHelper { public static bool CanBeSet(MemberInfo propertyOrField) { return propertyOrField is FieldInfo field ? !field.IsInitOnly : ((PropertyInfo)propertyOrField).CanWrite; } public static object GetDefaultValue(ParameterInfo parameter) { if (parameter.DefaultValue == null && parameter.ParameterType.IsValueType()) { return Activator.CreateInstance(parameter.ParameterType); } return parameter.DefaultValue; } public static object MapMember(ResolutionContext context, MemberInfo member, object value, object destination) { var memberType = GetMemberType(member); var destValue = GetMemberValue(member, destination); return context.Mapper.Map(value, destValue, value?.GetType() ?? memberType, memberType, context); } public static object MapMember(ResolutionContext context, MemberInfo member, object value) { var memberType = GetMemberType(member); return context.Mapper.Map(value, null, value?.GetType() ?? memberType, memberType, context); } public static bool IsDynamic(object obj) => obj is IDynamicMetaObjectProvider; public static bool IsDynamic(Type type) => typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type); public static void SetMemberValue(MemberInfo propertyOrField, object target, object value) { if (propertyOrField is PropertyInfo property) { property.SetValue(target, value, null); return; } if (propertyOrField is FieldInfo field) { field.SetValue(target, value); return; } throw Expected(propertyOrField); } private static ArgumentOutOfRangeException Expected(MemberInfo propertyOrField) => new ArgumentOutOfRangeException(nameof(propertyOrField), "Expected a property or field, not " + propertyOrField); public static object GetMemberValue(MemberInfo propertyOrField, object target) { if (propertyOrField is PropertyInfo property) { return property.GetValue(target, null); } if (propertyOrField is FieldInfo field) { return field.GetValue(target); } throw Expected(propertyOrField); } public static IEnumerable<MemberInfo> GetMemberPath(Type type, string fullMemberName) { MemberInfo property = null; foreach (var memberName in fullMemberName.Split('.')) { var currentType = GetCurrentType(property, type); yield return property = currentType.GetFieldOrProperty(memberName); } } private static Type GetCurrentType(MemberInfo member, Type type) { var memberType = member?.GetMemberType() ?? type; if (memberType.IsGenericType() && typeof(IEnumerable).IsAssignableFrom(memberType)) { memberType = memberType.GetTypeInfo().GenericTypeArguments[0]; } return memberType; } public static MemberInfo GetFieldOrProperty(LambdaExpression expression) { var memberExpression = expression.Body as MemberExpression; return memberExpression != null ? memberExpression.Member : throw new ArgumentOutOfRangeException(nameof(expression), "Expected a property/field access expression, not " + expression); } public static MemberInfo FindProperty(LambdaExpression lambdaExpression) { Expression expressionToCheck = lambdaExpression; var 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( $"Expression '{lambdaExpression}' must resolve to top-level member and not any child object's properties. You can use ForPath, a custom resolver on the child type or the AfterMap option instead.", nameof(lambdaExpression)); } var member = memberExpression.Member; return member; default: done = true; break; } } throw new AutoMapperConfigurationException( "Custom configuration for members is only supported for top-level individual members on a type."); } public static Type GetMemberType(MemberInfo memberInfo) { switch (memberInfo) { case MethodInfo mInfo: return mInfo.ReturnType; case PropertyInfo pInfo: return pInfo.PropertyType; case FieldInfo fInfo: return fInfo.FieldType; case null: throw new ArgumentNullException(nameof(memberInfo)); default: throw new ArgumentOutOfRangeException(nameof(memberInfo)); } } /// <summary> /// if targetType is oldType, method will return newType /// if targetType is not oldType, method will return targetType /// if targetType is generic type with oldType arguments, method will replace all oldType arguments on newType /// </summary> /// <param name="targetType"></param> /// <param name="oldType"></param> /// <param name="newType"></param> /// <returns></returns> public static Type ReplaceItemType(Type targetType, Type oldType, Type newType) { if (targetType == oldType) return newType; if (targetType.IsGenericType()) { var genSubArgs = targetType.GetTypeInfo().GenericTypeArguments; var newGenSubArgs = new Type[genSubArgs.Length]; for (var i = 0; i < genSubArgs.Length; i++) newGenSubArgs[i] = ReplaceItemType(genSubArgs[i], oldType, newType); return targetType.GetGenericTypeDefinition().MakeGenericType(newGenSubArgs); } return targetType; } } }