IndexExpression.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / Microsoft / Scripting / Ast / IndexExpression.cs / 1305376 / IndexExpression.cs

                            /* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Diagnostics;
using System.Dynamic.Utils; 
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
 
#if SILVERLIGHT
using System.Core; 
#endif 

namespace System.Linq.Expressions { 
    /// 
    /// Represents indexing a property or array.
    /// 
#if !SILVERLIGHT 
    [DebuggerTypeProxy(typeof(Expression.IndexExpressionProxy))]
#endif 
    public sealed class IndexExpression : Expression, IArgumentProvider { 
        private readonly Expression _instance;
        private readonly PropertyInfo _indexer; 
        private IList _arguments;

        internal IndexExpression(
            Expression instance, 
            PropertyInfo indexer,
            IList arguments) { 
 
            if (indexer == null) {
                Debug.Assert(instance != null && instance.Type.IsArray); 
                Debug.Assert(instance.Type.GetArrayRank() == arguments.Count);
            }

            _instance = instance; 
            _indexer = indexer;
            _arguments = arguments; 
        } 

        ///  
        /// Returns the node type of this . (Inherited from .)
        /// 
        /// The  that represents this expression.
        public sealed override ExpressionType NodeType { 
            get { return ExpressionType.Index; }
        } 
 
        /// 
        /// Gets the static type of the expression that this  represents. (Inherited from .) 
        /// 
        /// The  that represents the static type of the expression.
        public sealed override Type Type {
            get { 
                if (_indexer != null) {
                    return _indexer.PropertyType; 
                } 
                return _instance.Type.GetElementType();
            } 
        }

        /// 
        /// An object to index. 
        /// 
        public Expression Object { 
            get { return _instance; } 
        }
 
        /// 
        /// Gets the  for the property if the expression represents an indexed property, returns null otherwise.
        /// 
        public PropertyInfo Indexer { 
            get { return _indexer; }
        } 
 
        /// 
        /// Gets the arguments to be used to index the property or array. 
        /// 
        public ReadOnlyCollection Arguments {
            get { return ReturnReadOnly(ref _arguments); }
        } 

        ///  
        /// 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.
        /// The  property of the result.
        /// This expression if no children changed, or an expression with the updated children. 
        public IndexExpression Update(Expression @object, IEnumerable arguments) {
            if (@object == Object && arguments == Arguments) { 
                return this; 
            }
            return Expression.MakeIndex(@object, Indexer, arguments); 
        }

        Expression IArgumentProvider.GetArgument(int index) {
            return _arguments[index]; 
        }
 
        int IArgumentProvider.ArgumentCount { 
            get {
                return _arguments.Count; 
            }
        }

        ///  
        /// Dispatches to the specific visit method for this node type.
        ///  
        protected internal override Expression Accept(ExpressionVisitor visitor) { 
            return visitor.VisitIndex(this);
        } 

        internal Expression Rewrite(Expression instance, Expression[] arguments) {
            Debug.Assert(instance != null);
            Debug.Assert(arguments == null || arguments.Length == _arguments.Count); 

            return Expression.MakeIndex(instance, _indexer, arguments ?? _arguments); 
        } 
    }
 
    public partial class Expression {

        /// 
        /// Creates an  that represents accessing an indexed property in an object. 
        /// 
        /// The object to which the property belongs. Should be null if the property is static(shared). 
        /// An  representing the property to index. 
        /// An IEnumerable{Expression} contaning the arguments to be used to index the property.
        /// The created . 
        public static IndexExpression MakeIndex(Expression instance, PropertyInfo indexer, IEnumerable arguments) {
            if (indexer != null) {
                return Property(instance, indexer, arguments);
            } else { 
                return ArrayAccess(instance, arguments);
            } 
        } 

        #region ArrayAccess 

        /// 
        /// Creates an  to access an array.
        ///  
        /// An expression representing the array to index.
        /// An array containing expressions used to index the array. 
        /// The expression representing the array can be obtained by using the MakeMemberAccess method, 
        /// or through NewArrayBounds or NewArrayInit.
        /// The created . 
        public static IndexExpression ArrayAccess(Expression array, params Expression[] indexes) {
            return ArrayAccess(array, (IEnumerable)indexes);
        }
 
        /// 
        /// Creates an  to access an array. 
        ///  
        /// An expression representing the array to index.
        /// An  containing expressions used to index the array. 
        /// The expression representing the array can be obtained by using the MakeMemberAccess method,
        /// or through NewArrayBounds or NewArrayInit.
        /// The created .
        public static IndexExpression ArrayAccess(Expression array, IEnumerable indexes) { 
            RequiresCanRead(array, "array");
 
            Type arrayType = array.Type; 
            if (!arrayType.IsArray) {
                throw Error.ArgumentMustBeArray(); 
            }

            var indexList = indexes.ToReadOnly();
            if (arrayType.GetArrayRank() != indexList.Count) { 
                throw Error.IncorrectNumberOfIndexes();
            } 
 
            foreach (Expression e in indexList) {
                RequiresCanRead(e, "indexes"); 
                if (e.Type != typeof(int)) {
                    throw Error.ArgumentMustBeArrayIndexType();
                }
            } 

            return new IndexExpression(array, null, indexList); 
        } 

        #endregion 

        #region Property
        /// 
        /// Creates an  representing the access to an indexed property. 
        /// 
        /// The object to which the property belongs. If the property is static/shared, it must be null. 
        /// The name of the indexer. 
        /// An array of  objects that are used to index the property.
        /// The created . 
        public static IndexExpression Property(Expression instance, string propertyName, params Expression[] arguments) {
            RequiresCanRead(instance, "instance");
            ContractUtils.RequiresNotNull(propertyName, "indexerName");
            PropertyInfo pi = FindInstanceProperty(instance.Type, propertyName, arguments); 
            return Property(instance, pi, arguments);
        } 
 
        #region methods for finding a PropertyInfo by its name
        ///  
        /// The method finds the instance property with the specified name in a type. The property's type signature needs to be compatible with
        /// the arguments if it is a indexer. If the arguments is null or empty, we get a normal property.
        /// 
        private static PropertyInfo FindInstanceProperty(Type type, string propertyName, Expression[] arguments) { 
            // bind to public names first
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy; 
            PropertyInfo pi = FindProperty(type, propertyName, arguments, flags); 
            if (pi == null) {
                flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy; 
                pi = FindProperty(type, propertyName, arguments, flags);
            }
            if (pi == null) {
                if (arguments == null || arguments.Length == 0) { 
                    throw Error.InstancePropertyWithoutParameterNotDefinedForType(propertyName, type);
                } else { 
                    throw Error.InstancePropertyWithSpecifiedParametersNotDefinedForType(propertyName, GetArgTypesString(arguments), type); 
                }
            } 
            return pi;
        }

        private static string GetArgTypesString(Expression[] arguments) { 
            StringBuilder argTypesStr = new StringBuilder();
            var isFirst = true; 
            argTypesStr.Append("("); 
            foreach (var t in arguments.Select(arg => arg.Type)) {
                if (!isFirst) { 
                    argTypesStr.Append(", ");
                }
                argTypesStr.Append(t.Name);
                isFirst = false; 
            }
            argTypesStr.Append(")"); 
            return argTypesStr.ToString(); 
        }
 
        private static PropertyInfo FindProperty(Type type, string propertyName, Expression[] arguments, BindingFlags flags) {
            MemberInfo[] members = type.FindMembers(MemberTypes.Property, flags, Type.FilterNameIgnoreCase, propertyName);
            if (members == null || members.Length == 0)
                return null; 

            PropertyInfo pi; 
            var propertyInfos = members.Map(t => (PropertyInfo)t); 
            int count = FindBestProperty(propertyInfos, arguments, out pi);
 
            if (count == 0)
                return null;
            if (count > 1)
                throw Error.PropertyWithMoreThanOneMatch(propertyName, type); 
            return pi;
        } 
 
        private static int FindBestProperty(IEnumerable properties, Expression[] args, out PropertyInfo property) {
            int count = 0; 
            property = null;
            foreach (PropertyInfo pi in properties) {
                if (pi != null && IsCompatible(pi, args)) {
                    if (property == null) { 
                        property = pi;
                        count = 1; 
                    } 
                    else {
                        count++; 
                    }
                }
            }
            return count; 
        }
 
        private static bool IsCompatible(PropertyInfo pi, Expression[] args) { 
            MethodInfo mi;
 
            mi = pi.GetGetMethod(true);
            ParameterInfo[] parms;
            if (mi != null) {
                parms = mi.GetParametersCached(); 
            } else {
                mi = pi.GetSetMethod(true); 
                //The setter has an additional parameter for the value to set, 
                //need to remove the last type to match the arguments.
                parms = mi.GetParametersCached().RemoveLast(); 
            }

            if (mi == null) {
                return false; 
            }
            if (args == null) { 
                return parms.Length == 0; 
            }
 
            if (parms.Length != args.Length)
                return false;
            for (int i = 0; i < args.Length; i++) {
                if (args[i] == null) return false; 
                if (!TypeUtils.AreReferenceAssignable(parms[i].ParameterType, args[i].Type)) {
                    return false; 
                } 
            }
            return true; 
        }
        #endregion

        ///  
        /// Creates an  representing the access to an indexed property.
        ///  
        /// The object to which the property belongs. If the property is static/shared, it must be null. 
        /// The  that represents the property to index.
        /// An array of  objects that are used to index the property. 
        /// The created .
        public static IndexExpression Property(Expression instance, PropertyInfo indexer, params Expression[] arguments) {
            return Property(instance, indexer, (IEnumerable)arguments);
        } 

        ///  
        /// Creates an  representing the access to an indexed property. 
        /// 
        /// The object to which the property belongs. If the property is static/shared, it must be null. 
        /// The  that represents the property to index.
        /// An  of  objects that are used to index the property.
        /// The created .
        public static IndexExpression Property(Expression instance, PropertyInfo indexer, IEnumerable arguments) { 
            var argList = arguments.ToReadOnly();
            ValidateIndexedProperty(instance, indexer, ref argList); 
            return new IndexExpression(instance, indexer, argList); 
        }
 
        // CTS places no restrictions on properties (see ECMA-335 8.11.3),
        // so we validate that the property conforms to CLS rules here.
        //
        // Does reflection help us out at all? Expression.Property skips all of 
        // these checks, so either it needs more checks or we need less here.
        private static void ValidateIndexedProperty(Expression instance, PropertyInfo property, ref ReadOnlyCollection argList) { 
 
            // If both getter and setter specified, all their parameter types
            // should match, with exception of the last setter parameter which 
            // should match the type returned by the get method.
            // Accessor parameters cannot be ByRef.

            ContractUtils.RequiresNotNull(property, "property"); 
            if (property.PropertyType.IsByRef) throw Error.PropertyCannotHaveRefType();
            if (property.PropertyType == typeof(void)) throw Error.PropertyTypeCannotBeVoid(); 
 
            ParameterInfo[] getParameters = null;
            MethodInfo getter = property.GetGetMethod(true); 
            if (getter != null) {
                getParameters = getter.GetParametersCached();
                ValidateAccessor(instance, getter, getParameters, ref argList);
            } 

            MethodInfo setter = property.GetSetMethod(true); 
            if (setter != null) { 
                ParameterInfo[] setParameters = setter.GetParametersCached();
                if (setParameters.Length == 0) throw Error.SetterHasNoParams(); 

                // valueType is the type of the value passed to the setter (last parameter)
                Type valueType = setParameters[setParameters.Length - 1].ParameterType;
                if (valueType.IsByRef) throw Error.PropertyCannotHaveRefType(); 
                if (setter.ReturnType != typeof(void)) throw Error.SetterMustBeVoid();
                if (property.PropertyType != valueType) throw Error.PropertyTyepMustMatchSetter(); 
 
                if (getter != null) {
                    if (getter.IsStatic ^ setter.IsStatic) throw Error.BothAccessorsMustBeStatic(); 
                    if (getParameters.Length != setParameters.Length - 1) throw Error.IndexesOfSetGetMustMatch();

                    for (int i = 0; i < getParameters.Length; i++) {
                        if (getParameters[i].ParameterType != setParameters[i].ParameterType) throw Error.IndexesOfSetGetMustMatch(); 
                    }
                } else { 
                    ValidateAccessor(instance, setter, setParameters.RemoveLast(), ref argList); 
                }
            } 

            if (getter == null && setter == null) {
                throw Error.PropertyDoesNotHaveAccessor(property);
            } 
        }
 
        private static void ValidateAccessor(Expression instance, MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection arguments) { 
            ContractUtils.RequiresNotNull(arguments, "arguments");
 
            ValidateMethodInfo(method);
            if ((method.CallingConvention & CallingConventions.VarArgs) != 0) throw Error.AccessorsCannotHaveVarArgs();
            if (method.IsStatic) {
                if (instance != null) throw Error.OnlyStaticMethodsHaveNullInstance(); 
            } else {
                if (instance == null) throw Error.OnlyStaticMethodsHaveNullInstance(); 
                RequiresCanRead(instance, "instance"); 
                ValidateCallInstanceType(instance.Type, method);
            } 

            ValidateAccessorArgumentTypes(method, indexes, ref arguments);
        }
 
        private static void ValidateAccessorArgumentTypes(MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection arguments) {
            if (indexes.Length > 0) { 
                if (indexes.Length != arguments.Count) { 
                    throw Error.IncorrectNumberOfMethodCallArguments(method);
                } 
                Expression[] newArgs = null;
                for (int i = 0, n = indexes.Length; i < n; i++) {
                    Expression arg = arguments[i];
                    ParameterInfo pi = indexes[i]; 
                    RequiresCanRead(arg, "arguments");
 
                    Type pType = pi.ParameterType; 
                    if (pType.IsByRef) throw Error.AccessorsCannotHaveByRefArgs();
                    TypeUtils.ValidateType(pType); 

                    if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) {
                        if (TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), pType) && pType.IsAssignableFrom(arg.GetType())) {
                            arg = Expression.Quote(arg); 
                        } else {
                            throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); 
                        } 
                    }
                    if (newArgs == null && arg != arguments[i]) { 
                        newArgs = new Expression[arguments.Count];
                        for (int j = 0; j < i; j++) {
                            newArgs[j] = arguments[j];
                        } 
                    }
                    if (newArgs != null) { 
                        newArgs[i] = arg; 
                    }
                } 
                if (newArgs != null) {
                    arguments = new TrueReadOnlyCollection(newArgs);
                }
 
            } else if (arguments.Count > 0) {
                throw Error.IncorrectNumberOfMethodCallArguments(method); 
            } 
        }
 
        #endregion
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Diagnostics;
using System.Dynamic.Utils; 
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
 
#if SILVERLIGHT
using System.Core; 
#endif 

namespace System.Linq.Expressions { 
    /// 
    /// Represents indexing a property or array.
    /// 
#if !SILVERLIGHT 
    [DebuggerTypeProxy(typeof(Expression.IndexExpressionProxy))]
#endif 
    public sealed class IndexExpression : Expression, IArgumentProvider { 
        private readonly Expression _instance;
        private readonly PropertyInfo _indexer; 
        private IList _arguments;

        internal IndexExpression(
            Expression instance, 
            PropertyInfo indexer,
            IList arguments) { 
 
            if (indexer == null) {
                Debug.Assert(instance != null && instance.Type.IsArray); 
                Debug.Assert(instance.Type.GetArrayRank() == arguments.Count);
            }

            _instance = instance; 
            _indexer = indexer;
            _arguments = arguments; 
        } 

        ///  
        /// Returns the node type of this . (Inherited from .)
        /// 
        /// The  that represents this expression.
        public sealed override ExpressionType NodeType { 
            get { return ExpressionType.Index; }
        } 
 
        /// 
        /// Gets the static type of the expression that this  represents. (Inherited from .) 
        /// 
        /// The  that represents the static type of the expression.
        public sealed override Type Type {
            get { 
                if (_indexer != null) {
                    return _indexer.PropertyType; 
                } 
                return _instance.Type.GetElementType();
            } 
        }

        /// 
        /// An object to index. 
        /// 
        public Expression Object { 
            get { return _instance; } 
        }
 
        /// 
        /// Gets the  for the property if the expression represents an indexed property, returns null otherwise.
        /// 
        public PropertyInfo Indexer { 
            get { return _indexer; }
        } 
 
        /// 
        /// Gets the arguments to be used to index the property or array. 
        /// 
        public ReadOnlyCollection Arguments {
            get { return ReturnReadOnly(ref _arguments); }
        } 

        ///  
        /// 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.
        /// The  property of the result.
        /// This expression if no children changed, or an expression with the updated children. 
        public IndexExpression Update(Expression @object, IEnumerable arguments) {
            if (@object == Object && arguments == Arguments) { 
                return this; 
            }
            return Expression.MakeIndex(@object, Indexer, arguments); 
        }

        Expression IArgumentProvider.GetArgument(int index) {
            return _arguments[index]; 
        }
 
        int IArgumentProvider.ArgumentCount { 
            get {
                return _arguments.Count; 
            }
        }

        ///  
        /// Dispatches to the specific visit method for this node type.
        ///  
        protected internal override Expression Accept(ExpressionVisitor visitor) { 
            return visitor.VisitIndex(this);
        } 

        internal Expression Rewrite(Expression instance, Expression[] arguments) {
            Debug.Assert(instance != null);
            Debug.Assert(arguments == null || arguments.Length == _arguments.Count); 

            return Expression.MakeIndex(instance, _indexer, arguments ?? _arguments); 
        } 
    }
 
    public partial class Expression {

        /// 
        /// Creates an  that represents accessing an indexed property in an object. 
        /// 
        /// The object to which the property belongs. Should be null if the property is static(shared). 
        /// An  representing the property to index. 
        /// An IEnumerable{Expression} contaning the arguments to be used to index the property.
        /// The created . 
        public static IndexExpression MakeIndex(Expression instance, PropertyInfo indexer, IEnumerable arguments) {
            if (indexer != null) {
                return Property(instance, indexer, arguments);
            } else { 
                return ArrayAccess(instance, arguments);
            } 
        } 

        #region ArrayAccess 

        /// 
        /// Creates an  to access an array.
        ///  
        /// An expression representing the array to index.
        /// An array containing expressions used to index the array. 
        /// The expression representing the array can be obtained by using the MakeMemberAccess method, 
        /// or through NewArrayBounds or NewArrayInit.
        /// The created . 
        public static IndexExpression ArrayAccess(Expression array, params Expression[] indexes) {
            return ArrayAccess(array, (IEnumerable)indexes);
        }
 
        /// 
        /// Creates an  to access an array. 
        ///  
        /// An expression representing the array to index.
        /// An  containing expressions used to index the array. 
        /// The expression representing the array can be obtained by using the MakeMemberAccess method,
        /// or through NewArrayBounds or NewArrayInit.
        /// The created .
        public static IndexExpression ArrayAccess(Expression array, IEnumerable indexes) { 
            RequiresCanRead(array, "array");
 
            Type arrayType = array.Type; 
            if (!arrayType.IsArray) {
                throw Error.ArgumentMustBeArray(); 
            }

            var indexList = indexes.ToReadOnly();
            if (arrayType.GetArrayRank() != indexList.Count) { 
                throw Error.IncorrectNumberOfIndexes();
            } 
 
            foreach (Expression e in indexList) {
                RequiresCanRead(e, "indexes"); 
                if (e.Type != typeof(int)) {
                    throw Error.ArgumentMustBeArrayIndexType();
                }
            } 

            return new IndexExpression(array, null, indexList); 
        } 

        #endregion 

        #region Property
        /// 
        /// Creates an  representing the access to an indexed property. 
        /// 
        /// The object to which the property belongs. If the property is static/shared, it must be null. 
        /// The name of the indexer. 
        /// An array of  objects that are used to index the property.
        /// The created . 
        public static IndexExpression Property(Expression instance, string propertyName, params Expression[] arguments) {
            RequiresCanRead(instance, "instance");
            ContractUtils.RequiresNotNull(propertyName, "indexerName");
            PropertyInfo pi = FindInstanceProperty(instance.Type, propertyName, arguments); 
            return Property(instance, pi, arguments);
        } 
 
        #region methods for finding a PropertyInfo by its name
        ///  
        /// The method finds the instance property with the specified name in a type. The property's type signature needs to be compatible with
        /// the arguments if it is a indexer. If the arguments is null or empty, we get a normal property.
        /// 
        private static PropertyInfo FindInstanceProperty(Type type, string propertyName, Expression[] arguments) { 
            // bind to public names first
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy; 
            PropertyInfo pi = FindProperty(type, propertyName, arguments, flags); 
            if (pi == null) {
                flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy; 
                pi = FindProperty(type, propertyName, arguments, flags);
            }
            if (pi == null) {
                if (arguments == null || arguments.Length == 0) { 
                    throw Error.InstancePropertyWithoutParameterNotDefinedForType(propertyName, type);
                } else { 
                    throw Error.InstancePropertyWithSpecifiedParametersNotDefinedForType(propertyName, GetArgTypesString(arguments), type); 
                }
            } 
            return pi;
        }

        private static string GetArgTypesString(Expression[] arguments) { 
            StringBuilder argTypesStr = new StringBuilder();
            var isFirst = true; 
            argTypesStr.Append("("); 
            foreach (var t in arguments.Select(arg => arg.Type)) {
                if (!isFirst) { 
                    argTypesStr.Append(", ");
                }
                argTypesStr.Append(t.Name);
                isFirst = false; 
            }
            argTypesStr.Append(")"); 
            return argTypesStr.ToString(); 
        }
 
        private static PropertyInfo FindProperty(Type type, string propertyName, Expression[] arguments, BindingFlags flags) {
            MemberInfo[] members = type.FindMembers(MemberTypes.Property, flags, Type.FilterNameIgnoreCase, propertyName);
            if (members == null || members.Length == 0)
                return null; 

            PropertyInfo pi; 
            var propertyInfos = members.Map(t => (PropertyInfo)t); 
            int count = FindBestProperty(propertyInfos, arguments, out pi);
 
            if (count == 0)
                return null;
            if (count > 1)
                throw Error.PropertyWithMoreThanOneMatch(propertyName, type); 
            return pi;
        } 
 
        private static int FindBestProperty(IEnumerable properties, Expression[] args, out PropertyInfo property) {
            int count = 0; 
            property = null;
            foreach (PropertyInfo pi in properties) {
                if (pi != null && IsCompatible(pi, args)) {
                    if (property == null) { 
                        property = pi;
                        count = 1; 
                    } 
                    else {
                        count++; 
                    }
                }
            }
            return count; 
        }
 
        private static bool IsCompatible(PropertyInfo pi, Expression[] args) { 
            MethodInfo mi;
 
            mi = pi.GetGetMethod(true);
            ParameterInfo[] parms;
            if (mi != null) {
                parms = mi.GetParametersCached(); 
            } else {
                mi = pi.GetSetMethod(true); 
                //The setter has an additional parameter for the value to set, 
                //need to remove the last type to match the arguments.
                parms = mi.GetParametersCached().RemoveLast(); 
            }

            if (mi == null) {
                return false; 
            }
            if (args == null) { 
                return parms.Length == 0; 
            }
 
            if (parms.Length != args.Length)
                return false;
            for (int i = 0; i < args.Length; i++) {
                if (args[i] == null) return false; 
                if (!TypeUtils.AreReferenceAssignable(parms[i].ParameterType, args[i].Type)) {
                    return false; 
                } 
            }
            return true; 
        }
        #endregion

        ///  
        /// Creates an  representing the access to an indexed property.
        ///  
        /// The object to which the property belongs. If the property is static/shared, it must be null. 
        /// The  that represents the property to index.
        /// An array of  objects that are used to index the property. 
        /// The created .
        public static IndexExpression Property(Expression instance, PropertyInfo indexer, params Expression[] arguments) {
            return Property(instance, indexer, (IEnumerable)arguments);
        } 

        ///  
        /// Creates an  representing the access to an indexed property. 
        /// 
        /// The object to which the property belongs. If the property is static/shared, it must be null. 
        /// The  that represents the property to index.
        /// An  of  objects that are used to index the property.
        /// The created .
        public static IndexExpression Property(Expression instance, PropertyInfo indexer, IEnumerable arguments) { 
            var argList = arguments.ToReadOnly();
            ValidateIndexedProperty(instance, indexer, ref argList); 
            return new IndexExpression(instance, indexer, argList); 
        }
 
        // CTS places no restrictions on properties (see ECMA-335 8.11.3),
        // so we validate that the property conforms to CLS rules here.
        //
        // Does reflection help us out at all? Expression.Property skips all of 
        // these checks, so either it needs more checks or we need less here.
        private static void ValidateIndexedProperty(Expression instance, PropertyInfo property, ref ReadOnlyCollection argList) { 
 
            // If both getter and setter specified, all their parameter types
            // should match, with exception of the last setter parameter which 
            // should match the type returned by the get method.
            // Accessor parameters cannot be ByRef.

            ContractUtils.RequiresNotNull(property, "property"); 
            if (property.PropertyType.IsByRef) throw Error.PropertyCannotHaveRefType();
            if (property.PropertyType == typeof(void)) throw Error.PropertyTypeCannotBeVoid(); 
 
            ParameterInfo[] getParameters = null;
            MethodInfo getter = property.GetGetMethod(true); 
            if (getter != null) {
                getParameters = getter.GetParametersCached();
                ValidateAccessor(instance, getter, getParameters, ref argList);
            } 

            MethodInfo setter = property.GetSetMethod(true); 
            if (setter != null) { 
                ParameterInfo[] setParameters = setter.GetParametersCached();
                if (setParameters.Length == 0) throw Error.SetterHasNoParams(); 

                // valueType is the type of the value passed to the setter (last parameter)
                Type valueType = setParameters[setParameters.Length - 1].ParameterType;
                if (valueType.IsByRef) throw Error.PropertyCannotHaveRefType(); 
                if (setter.ReturnType != typeof(void)) throw Error.SetterMustBeVoid();
                if (property.PropertyType != valueType) throw Error.PropertyTyepMustMatchSetter(); 
 
                if (getter != null) {
                    if (getter.IsStatic ^ setter.IsStatic) throw Error.BothAccessorsMustBeStatic(); 
                    if (getParameters.Length != setParameters.Length - 1) throw Error.IndexesOfSetGetMustMatch();

                    for (int i = 0; i < getParameters.Length; i++) {
                        if (getParameters[i].ParameterType != setParameters[i].ParameterType) throw Error.IndexesOfSetGetMustMatch(); 
                    }
                } else { 
                    ValidateAccessor(instance, setter, setParameters.RemoveLast(), ref argList); 
                }
            } 

            if (getter == null && setter == null) {
                throw Error.PropertyDoesNotHaveAccessor(property);
            } 
        }
 
        private static void ValidateAccessor(Expression instance, MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection arguments) { 
            ContractUtils.RequiresNotNull(arguments, "arguments");
 
            ValidateMethodInfo(method);
            if ((method.CallingConvention & CallingConventions.VarArgs) != 0) throw Error.AccessorsCannotHaveVarArgs();
            if (method.IsStatic) {
                if (instance != null) throw Error.OnlyStaticMethodsHaveNullInstance(); 
            } else {
                if (instance == null) throw Error.OnlyStaticMethodsHaveNullInstance(); 
                RequiresCanRead(instance, "instance"); 
                ValidateCallInstanceType(instance.Type, method);
            } 

            ValidateAccessorArgumentTypes(method, indexes, ref arguments);
        }
 
        private static void ValidateAccessorArgumentTypes(MethodInfo method, ParameterInfo[] indexes, ref ReadOnlyCollection arguments) {
            if (indexes.Length > 0) { 
                if (indexes.Length != arguments.Count) { 
                    throw Error.IncorrectNumberOfMethodCallArguments(method);
                } 
                Expression[] newArgs = null;
                for (int i = 0, n = indexes.Length; i < n; i++) {
                    Expression arg = arguments[i];
                    ParameterInfo pi = indexes[i]; 
                    RequiresCanRead(arg, "arguments");
 
                    Type pType = pi.ParameterType; 
                    if (pType.IsByRef) throw Error.AccessorsCannotHaveByRefArgs();
                    TypeUtils.ValidateType(pType); 

                    if (!TypeUtils.AreReferenceAssignable(pType, arg.Type)) {
                        if (TypeUtils.IsSameOrSubclass(typeof(LambdaExpression), pType) && pType.IsAssignableFrom(arg.GetType())) {
                            arg = Expression.Quote(arg); 
                        } else {
                            throw Error.ExpressionTypeDoesNotMatchMethodParameter(arg.Type, pType, method); 
                        } 
                    }
                    if (newArgs == null && arg != arguments[i]) { 
                        newArgs = new Expression[arguments.Count];
                        for (int j = 0; j < i; j++) {
                            newArgs[j] = arguments[j];
                        } 
                    }
                    if (newArgs != null) { 
                        newArgs[i] = arg; 
                    }
                } 
                if (newArgs != null) {
                    arguments = new TrueReadOnlyCollection(newArgs);
                }
 
            } else if (arguments.Count > 0) {
                throw Error.IncorrectNumberOfMethodCallArguments(method); 
            } 
        }
 
        #endregion
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK