예제 #1
0
        /// <summary>
        /// Appends the fields and values defined by the given object of the given Class.
        /// </summary>
        /// <param name="builder">the builder to Append to</param>
        /// <param name="clazz">the class to Append details of</param>
        /// <param name="lhs">the left hand object</param>
        /// <param name="rhs">the right hand object</param>
        /// <param name="useTransients">whether to test transient fields</param>
        private static void ReflectionAppend(
            Object lhs,
            Object rhs,
            Type clazz,
            EqualsBuilder builder,
            bool useTransients)
        {
            /*
             * In Java version of this ReflectionAppend, we have to call
             * AccessibleObject.setAccessible() right after class.GetFields() to
             * make non-public fields accessible. In C#, it is easier to do. We simply
             * add BindingFlags.NonPublic, which makes non-public fields accessible
             * (subject to security manager restrictions, of course).
             */
            FieldInfo[] fields = clazz.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
            for (int i = 0; i < fields.Length && builder.isEqual; i++)
            {
                FieldInfo f = fields[i];
                if ((f.Name.IndexOf('$') == -1) &&
                    (useTransients || !isTransient(f)) &&
                    !f.IsStatic)
                {
                    try
                    {
                        builder.Append(f.GetValue(lhs), f.GetValue(rhs));
                    }

                    /*
                     * According to FieldInfo's documentation, getValue() can throw the
                     * following exceptions: TargetException, NotSupportedException,
                     * FieldAccessException and ArgumentException.
                     *
                     * TargetException is thrown if the field is non-static and obj is
                     * a null reference. In our case, the field is non-static (because of
                     * BindingFlags.Instance) but obj should never be null because of
                     * null checks in the calling method (i.e. reflectionEquals()).
                     * I guess we can just throw an unexpected exception.
                     *
                     * NotSupportedException is thrown if the field is marked Literal, but
                     * the field does not have one of the accepted literal types. Literal
                     * means that the field's value is a compile-time (static or early
                     * bound) constant. I think this exception can be just eaten because
                     * constants should always be equal in lhs and rhs and default value
                     * of isEqual is true.
                     *
                     * FieldAccessException is thrown if the caller does not have
                     * permission to access this field. If this code is fully trusted
                     * (not sure how to verify this), access restrictions are ignored.
                     * This means that private fields are accessible. If this code is not
                     * fully trusted, it can still access private fields if this code
                     * has been granted ReflectedPermission with the
                     * ReflectionPermisionFlag.RestrictedMemberAccess flag and if the
                     * grant set of the non-public members is restricted to the caller's
                     * grant set, or subset thereof. Whew, that's a mouthful to say!
                     * I guess it's best just to let FieldAccessException bubble up
                     * to callers so that user can grant permissions, if desired.
                     *
                     * Finally, ArgumentException is thrown if the method is neither
                     * declared nor inherited by the class of obj. This could happen
                     * if lhs is a subclass of rhs (or vice-versa) and the field is
                     * declared in the subclass. In Java, Field.get() would throw
                     * IllegalArgumentException in this case. In Java version of
                     * reflectionAppend(), IllegalArgumentException
                     * bubbles up to reflectionEquals(), where it is dealt with.
                     * It seems logical that use the same technique in the C#
                     * version. That is, we allow ArgumentException to bubble up
                     * to ReflectionEquals() and deal with it there.
                     */
                    catch (TargetException te)
                    {
                        throw new Exception("Unexpected TargetException", te);
                    }
                    catch (NotSupportedException nse)
                    {
                        // eat it!
                    }

                    /* Don't catch FieldAccessException and ArgumentException so that
                     * they can bubble up to caller. Alternatively, we could catch and
                     * rethrow.
                     */
                    //catch (FieldAccessException fae) { throw; }
                    //catch (ArgumentException fae) { throw; }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// This method uses reflection to determine if the two Object
        /// are equal.
        ///
        /// It uses AccessibleObject.setAccessible to gain access to private
        /// fields. This means that it will throw a security exception if run under
        /// a security manager, if the permissions are not set up correctly. It is also
        /// not as efficient as testing explicitly.
        ///
        /// If the testTransients parameter is set to true, transient
        /// members will be tested, otherwise they are ignored, as they are likely
        /// derived fields, and not part of the value of the Object.
        ///
        /// Static fields will not be included. Superclass fields will be appended
        /// up to and including the specified superclass. A null superclass is treated
        /// as java.lang.Object.
        /// </summary>
        /// <param name="lhs">this object</param>
        /// <param name="rhs">the other object</param>
        /// <param name="testTransients">whether to include transient fields</param>
        /// <param name="reflectUpToClass">the superclass to reflect up to (inclusive), may be null</param>
        /// <returns>true if the two Objects have tested equals.</returns>
        public static bool ReflectionEquals(Object lhs, Object rhs, bool testTransients, Type reflectUpToClass)
        {
            if (lhs == rhs)
            {
                return(true);
            }
            if (lhs == null || rhs == null)
            {
                return(false);
            }
            // Find the leaf class since there may be transients in the leaf
            // class or in classes between the leaf and root.
            // If we are not testing transients or a subclass has no ivars,
            // then a subclass can test equals to a superclass.
            Type lhsClass = lhs.GetType();
            Type rhsClass = rhs.GetType();
            Type testClass;

            if (lhsClass.IsInstanceOfType(rhs))
            {
                testClass = lhsClass;
                if (!rhsClass.IsInstanceOfType(lhs))
                {
                    // rhsClass is a subclass of lhsClass
                    testClass = rhsClass;
                }
            }
            else if (rhsClass.IsInstanceOfType(lhs))
            {
                testClass = rhsClass;
                if (!lhsClass.IsInstanceOfType(rhs))
                {
                    // lhsClass is a subclass of rhsClass
                    testClass = lhsClass;
                }
            }
            else
            {
                // The two classes are not related.
                return(false);
            }
            EqualsBuilder equalsBuilder = new EqualsBuilder();

            try
            {
                ReflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients);
                while (testClass.BaseType != null && testClass != reflectUpToClass)
                {
                    testClass = testClass.BaseType;
                    ReflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients);
                }
            }
            catch (ArgumentException e)
            {
                // In this case, we tried to test a subclass vs. a superclass and
                // the subclass has ivars or the ivars are transient and
                // we are testing transients.
                // If a subclass has ivars that we are trying to test them, we get an
                // exception and we know that the objects are not equal.
                return(false);
            }
            return(equalsBuilder.IsEquals());
        }