#if NET20 || NET30 || !NET_4_6 // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Diagnostics; using System.Dynamic.Utils; using System.Reflection; using LinqInternal.Core; namespace System.Linq.Expressions.Reimplement { /// /// Represents accessing a field or property. /// [DebuggerTypeProxy(typeof(MemberExpressionProxy))] public class MemberExpression : Expression { private readonly Expression _expression; /// /// Gets the field or property to be accessed. /// public MemberInfo Member { get { return GetMember(); } } /// /// Gets the containing object of the field or property. /// public Expression Expression { get { return _expression; } } // param order: factories args in order, then other args internal MemberExpression(Expression expression) { _expression = expression; } internal static MemberExpression Make(Expression expression, MemberInfo member) { var fi = member as FieldInfo; if (fi != null) { return new FieldExpression(expression, fi); } else { var pi = (PropertyInfo)member; return new PropertyExpression(expression, pi); } } /// /// Returns the node type of this . (Inherited from .) /// /// The that represents this expression. public sealed override ExpressionType NodeType { get { return ExpressionType.MemberAccess; } } internal virtual MemberInfo GetMember() { throw ContractUtils.Unreachable; } protected internal override Expression Accept(ExpressionVisitor visitor) { return visitor.VisitMember(this); } /// /// Creates a new expression that is like this one, but using the /// supplied children. If all of the children are the same, it will /// return this expression. /// /// The property of the result. /// This expression if no children changed, or an expression with the updated children. public MemberExpression Update(Expression expression) { if (expression == Expression) { return this; } return MakeMemberAccess(expression, Member); } } internal class FieldExpression : MemberExpression { private readonly FieldInfo _field; public FieldExpression(Expression expression, FieldInfo member) : base(expression) { _field = member; } internal override MemberInfo GetMember() { return _field; } public sealed override Type Type { get { return _field.FieldType; } } } internal class PropertyExpression : MemberExpression { private readonly PropertyInfo _property; public PropertyExpression(Expression expression, PropertyInfo member) : base(expression) { _property = member; } internal override MemberInfo GetMember() { return _property; } public sealed override Type Type { get { return _property.PropertyType; } } } public partial class Expression { private const BindingFlags _bindingFlags = BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy; private const BindingFlags _staticFlags = BindingFlags.Static | _bindingFlags; private const BindingFlags _publicFlags = BindingFlags.Public | _bindingFlags; private const BindingFlags _nonPublicFlags = BindingFlags.NonPublic | _bindingFlags; private const BindingFlags _publicStaticFlags = BindingFlags.Public | _staticFlags; private const BindingFlags _nonPublicStaticFlags = BindingFlags.NonPublic | _staticFlags; #region Field /// /// Creates a accessing a field. /// /// The containing object of the field. This can be null for static fields. /// The field to be accessed. /// The created . public static MemberExpression Field(Expression expression, FieldInfo field) { ContractUtils.RequiresNotNull(field, "field"); if (field.IsStatic) { if (expression != null) { throw new ArgumentException(Strings.OnlyStaticFieldsHaveNullInstance, "expression"); } } else { if (expression == null) { throw new ArgumentException(Strings.OnlyStaticFieldsHaveNullInstance, "field"); } RequiresCanRead(expression, "expression"); if (!TypeHelper.AreReferenceAssignable(field.DeclaringType, expression.Type)) { throw Error.FieldInfoNotDefinedForType(field.DeclaringType, field.Name, expression.Type); } } return MemberExpression.Make(expression, field); } /// /// Creates a accessing a field. /// /// The containing object of the field. This can be null for static fields. /// The field to be accessed. /// The created . public static MemberExpression Field(Expression expression, string fieldName) { RequiresCanRead(expression, "expression"); // bind to public names first var fi = expression.Type.GetField(fieldName, _publicFlags) ?? expression.Type.GetField(fieldName, _nonPublicFlags); if (fi == null) { throw Error.InstanceFieldNotDefinedForType(fieldName, expression.Type); } return Field(expression, fi); } /// /// Creates a accessing a field. /// /// The containing object of the field. This can be null for static fields. /// The containing the field. /// The field to be accessed. /// The created . public static MemberExpression Field(Expression expression, Type type, string fieldName) { ContractUtils.RequiresNotNull(type, "type"); // bind to public names first var fi = type.GetField(fieldName, _publicStaticFlags); if (fi == null) { fi = type.GetField(fieldName, _nonPublicStaticFlags); } if (fi == null) { throw Error.FieldNotDefinedForType(fieldName, type); } return Field(expression, fi); } #endregion Field #region Property /// /// Creates a accessing a property. /// /// The containing object of the property. This can be null for static properties. /// The property to be accessed. /// The created . public static MemberExpression Property(Expression expression, string propertyName) { RequiresCanRead(expression, "expression"); ContractUtils.RequiresNotNull(propertyName, "propertyName"); // bind to public names first var pi = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); if (pi == null) { pi = expression.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); } if (pi == null) { throw Error.InstancePropertyNotDefinedForType(propertyName, expression.Type); } return Property(expression, pi); } /// /// Creates a accessing a property. /// /// The containing object of the property. This can be null for static properties. /// The containing the property. /// The property to be accessed. /// The created . public static MemberExpression Property(Expression expression, Type type, string propertyName) { ContractUtils.RequiresNotNull(type, "type"); ContractUtils.RequiresNotNull(propertyName, "propertyName"); // bind to public names first var pi = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); if (pi == null) { pi = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy); } if (pi == null) { throw Error.PropertyNotDefinedForType(propertyName, type); } return Property(expression, pi); } /// /// Creates a accessing a property. /// /// The containing object of the property. This can be null for static properties. /// The property to be accessed. /// The created . public static MemberExpression Property(Expression expression, PropertyInfo property) { ContractUtils.RequiresNotNull(property, "property"); var mi = property.GetGetMethod(true) ?? property.GetSetMethod(true); if (mi == null) { throw Error.PropertyDoesNotHaveAccessor(property); } if (mi.IsStatic) { if (expression != null) { throw new ArgumentException(Strings.OnlyStaticPropertiesHaveNullInstance, "expression"); } } else { if (expression == null) { throw new ArgumentException(Strings.OnlyStaticPropertiesHaveNullInstance, "property"); } RequiresCanRead(expression, "expression"); if (!TypeHelper.IsValidInstanceType(property, expression.Type)) { throw Error.PropertyNotDefinedForType(property, expression.Type); } } return MemberExpression.Make(expression, property); } /// /// Creates a accessing a property. /// /// The containing object of the property. This can be null for static properties. /// An accessor method of the property to be accessed. /// The created . public static MemberExpression Property(Expression expression, MethodInfo propertyAccessor) { ContractUtils.RequiresNotNull(propertyAccessor, "propertyAccessor"); ValidateMethodInfo(propertyAccessor); return Property(expression, GetProperty(propertyAccessor)); } private static PropertyInfo GetProperty(MethodInfo mi) { var type = mi.DeclaringType; var flags = BindingFlags.Public | BindingFlags.NonPublic; flags |= (mi.IsStatic) ? BindingFlags.Static : BindingFlags.Instance; var props = type.GetProperties(flags); foreach (var pi in props) { if (pi.CanRead && CheckMethod(mi, pi.GetGetMethod(true))) { return pi; } if (pi.CanWrite && CheckMethod(mi, pi.GetSetMethod(true))) { return pi; } } throw Error.MethodNotPropertyAccessor(mi.DeclaringType, mi.Name); } private static bool CheckMethod(MethodInfo method, MethodInfo propertyMethod) { if (method.Equals(propertyMethod)) { return true; } // If the type is an interface then the handle for the method got by the compiler will not be the // same as that returned by reflection. // Check for this condition and try and get the method from reflection. var type = method.DeclaringType; if (type.IsInterface && method.Name == propertyMethod.Name && type.GetMethod(method.Name) == propertyMethod) { return true; } return false; } #endregion Property /// /// Creates a accessing a property or field. /// /// The containing object of the member. This can be null for static members. /// The member to be accessed. /// The created . public static MemberExpression PropertyOrField(Expression expression, string propertyOrFieldName) { RequiresCanRead(expression, "expression"); // bind to public names first var pi = expression.Type.GetProperty(propertyOrFieldName, _publicFlags); if (pi != null) { return Property(expression, pi); } var fi = expression.Type.GetField(propertyOrFieldName, _publicFlags); if (fi != null) { return Field(expression, fi); } pi = expression.Type.GetProperty(propertyOrFieldName, _nonPublicFlags); if (pi != null) { return Property(expression, pi); } fi = expression.Type.GetField(propertyOrFieldName, _nonPublicFlags); if (fi != null) { return Field(expression, fi); } throw Error.NotAMemberOfType(propertyOrFieldName, expression.Type); } /// /// Creates a accessing a property or field. /// /// The containing object of the member. This can be null for static members. /// The member to be accessed. /// The created . public static MemberExpression MakeMemberAccess(Expression expression, MemberInfo member) { ContractUtils.RequiresNotNull(member, "member"); var fi = member as FieldInfo; if (fi != null) { return Field(expression, fi); } var pi = member as PropertyInfo; if (pi != null) { return Property(expression, pi); } throw Error.MemberNotFieldOrProperty(member); } } } #endif