What is the algorithm used by the memberwise equality test in .NET structs?

前端 未结 5 1438
再見小時候
再見小時候 2021-02-07 16:04

What is the algorithm used by the memberwise equality test in .NET structs? I would like to know this so that I can use it as the basis for my own algorithm.

I am trying

5条回答
  •  余生分开走
    2021-02-07 16:29

    Here's my own attempt at this problem. It works, but I'm not convinced I've covered all the bases.

    public class MemberwiseEqualityComparer : IEqualityComparer
    {
        public bool Equals(object x, object y)
        {
            // ----------------------------------------------------------------
            // 1. If exactly one is null, return false.
            // 2. If they are the same reference, then they must be equal by
            //    definition.
            // 3. If the objects are both IEnumerable, return the result of
            //    comparing each item.
            // 4. If the objects are equatable, return the result of comparing
            //    them.
            // 5. If the objects are different types, return false.
            // 6. Iterate over the public properties and compare them. If there
            //    is a pair that are not equal, return false.
            // 7. Return true.
            // ----------------------------------------------------------------
    
            //
            // 1. If exactly one is null, return false.
            //
            if (null == x ^ null == y) return false;
    
            //
            // 2. If they are the same reference, then they must be equal by
            //    definition.
            //
            if (object.ReferenceEquals(x, y)) return true;
    
            //
            // 3. If the objects are both IEnumerable, return the result of
            //    comparing each item.
            // For collections, we want to compare the contents rather than
            // the properties of the collection itself so we check if the
            // classes are IEnumerable instances before we check to see that
            // they are the same type.
            //
            if (x is IEnumerable && y is IEnumerable && false == x is string)
            {
                return contentsAreEqual((IEnumerable)x, (IEnumerable)y);
            }
    
            //
            // 4. If the objects are equatable, return the result of comparing
            //    them.
            // We are assuming that the type of X implements IEquatable<> of itself
            // (see below) which is true for the numeric types and string.
            // e.g.: public class TypeOfX : IEquatable { ... }
            //
            var xType = x.GetType();
            var yType = y.GetType();
            var equatableType = typeof(IEquatable<>).MakeGenericType(xType);
            if (equatableType.IsAssignableFrom(xType)
                && xType.IsAssignableFrom(yType))
            {
                return equatablesAreEqual(equatableType, x, y);
            }
    
            //
            // 5. If the objects are different types, return false.
            //
            if (xType != yType) return false;
    
            //
            // 6. Iterate over the public properties and compare them. If there
            //    is a pair that are not equal, return false.
            //
            if (false == propertiesAndFieldsAreEqual(x, y)) return false;
    
            //
            // 7. Return true.
            //
            return true;
        }
    
        public int GetHashCode(object obj)
        {
            return null != obj ? obj.GetHashCode() : 0;
        }
    
        private bool contentsAreEqual(IEnumerable enumX, IEnumerable enumY)
        {
            var enumOfObjX = enumX.OfType();
            var enumOfObjY = enumY.OfType();
    
            if (enumOfObjX.Count() != enumOfObjY.Count()) return false;
    
            var contentsAreEqual = enumOfObjX
                .Zip(enumOfObjY) // Custom Zip extension which returns
                                 // Pair. Similar to .NET 4's Zip
                                 // extension.
                .All(pair => Equals(pair.First, pair.Second))
                ;
    
            return contentsAreEqual;
        }
    
        private bool equatablesAreEqual(Type equatableType, object x, object y)
        {
            var equalsMethod = equatableType.GetMethod("Equals");
            var equal = (bool)equalsMethod.Invoke(x, new[] { y });
            return equal;
        }
    
        private bool propertiesAndFieldsAreEqual(object x, object y)
        {
            const BindingFlags bindingFlags
                = BindingFlags.Public | BindingFlags.Instance;
    
            var propertyValues = from pi in x.GetType()
                                             .GetProperties(bindingFlags)
                                             .AsQueryable()
                                 where pi.CanRead
                                 select new
                                 {
                                     Name   = pi.Name,
                                     XValue = pi.GetValue(x, null),
                                     YValue = pi.GetValue(y, null),
                                 };
    
            var fieldValues = from fi in x.GetType()
                                          .GetFields(bindingFlags)
                                          .AsQueryable()
                              select new
                              {
                                  Name   = fi.Name,
                                  XValue = fi.GetValue(x),
                                  YValue = fi.GetValue(y),
                              };
    
            var propertiesAreEqual = propertyValues.Union(fieldValues)
                .All(v => Equals(v.XValue, v.YValue))
                ;
    
            return propertiesAreEqual;
        }
    }
    
        

    提交回复
    热议问题