public void Compare_null_references()
        {
            var comparer = new StructuralEqualityComparer <Foo>
            {
                { x => x.Number },
            };

            var foo = new Foo()
            {
                Number = 123, Text = "Hello", Duration = TimeSpan.FromMinutes(600)
            };

            Assert.IsFalse(comparer.Equals(foo, null));
            Assert.IsFalse(comparer.Equals(null, foo));
            Assert.IsTrue(comparer.Equals(null, null));
        }
        public void Compare_Int32_parity(int value1, int value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<int>
            {
                { x => x % 2, (x, y) => x == y }
            };

            bool result = comparer.Equals(value1, value2);
            Assert.AreEqual(expected, result);
        }
        public void Compare_Int32_parity(int value1, int value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <int>
            {
                { x => x % 2, (x, y) => x == y }
            };

            bool result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Compare_null_enumerations(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<Foo>
            {
                { x => x.Children, new StructuralEqualityComparer<ChildFoo> { x => x.Tag } }
            };

            var result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Nested_comparer_on_child_enumeration_ignoring_order(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <Foo>
            {
                { x => x.Number },
                { x => x.Values, StructuralEqualityComparer <int> .Default, StructuralEqualityComparerOptions.IgnoreEnumerableOrder }
            };

            bool result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Strict_nested_comparer_on_child_enumeration(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <Foo>
            {
                { x => x.Number },
                { x => x.Values, StructuralEqualityComparer <int> .Default }
            };

            bool result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Compare_String_length_and_first_characters(string value1, string value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <string>
            {
                { x => x.Length, (x, y) => x == y },
                { x => x.Substring(0, 3), (x, y) => String.Compare(x, y, true) == 0 }
            };

            bool result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Compare_null_enumerations(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <Foo>
            {
                { x => x.Children, new StructuralEqualityComparer <ChildFoo> {
                      x => x.Tag
                  } }
            };

            var result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Compare_null_enumerations_ignoring_order(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <Foo>
            {
                { x => x.Children, new StructuralEqualityComparer <ChildFoo> {
                      x => x.Tag
                  }, StructuralEqualityComparerOptions.IgnoreEnumerableOrder }
            };

            var result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Complex_comparers_with_nested_enumerations(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <Foo>
            {
                { x => x.Number % 2 },
                { x => x.Text, (x, y) => String.Compare(x, y, true) == 0 },
                { x => x.Values, StructuralEqualityComparer <int> .Default, StructuralEqualityComparerOptions.IgnoreEnumerableOrder },
                { x => x.Children, new StructuralEqualityComparer <ChildFoo> {
                      { x => x.Tag }
                  }, StructuralEqualityComparerOptions.IgnoreEnumerableOrder }
            };

            bool result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Nested_comparers_on_complex_type(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer <Foo>
            {
                { x => x.Number },

                { (x, y) => String.Compare(x.Text, y.Text, true) == 0 },

                { x => x.Duration,
                  new StructuralEqualityComparer <TimeSpan>
                  {
                      { x => x.TotalHours, (x, y) => Math.Abs(x - y) <= 1 }
                  } }
            };

            bool result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Strict_nested_comparer_on_child_enumeration(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<Foo>
            {
                { x => x.Number },
                { x => x.Values,  StructuralEqualityComparer<int>.Default }
            };

            bool result = comparer.Equals(value1, value2);
            Assert.AreEqual(expected, result);
        }
        public void Nested_comparer_on_child_enumeration_ignoring_order(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<Foo>
            {
                { x => x.Number },
                { x => x.Values,  StructuralEqualityComparer<int>.Default, StructuralEqualityComparerOptions.IgnoreEnumerableOrder }
            };

            bool result = comparer.Equals(value1, value2);
            Assert.AreEqual(expected, result);
        }
        public void Nested_comparers_on_complex_type(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<Foo>
            {
                { x => x.Number },

                { (x, y) => String.Compare(x.Text, y.Text, true) == 0 },

                { x => x.Duration,
                    new StructuralEqualityComparer<TimeSpan>
                    {
                        { x => x.TotalHours, (x, y) => Math.Abs(x - y) <= 1 }
                    }
                }
            };

            bool result = comparer.Equals(value1, value2);
            Assert.AreEqual(expected, result);
        }
        public void Complex_comparers_with_nested_enumerations(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<Foo>
            {
                { x => x.Number % 2 },
                { x => x.Text, (x, y) => String.Compare(x, y, true) == 0 },
                { x => x.Values, StructuralEqualityComparer<int>.Default, StructuralEqualityComparerOptions.IgnoreEnumerableOrder },
                { x => x.Children, new StructuralEqualityComparer<ChildFoo> { { x => x.Tag } }, StructuralEqualityComparerOptions.IgnoreEnumerableOrder }
            };

            bool result = comparer.Equals(value1, value2);
            Assert.AreEqual(expected, result);
        }
        public void Compare_String_length_and_first_characters(string value1, string value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<string>
            {
                { x => x.Length, (x, y) => x == y },
                { x => x.Substring(0, 3), (x, y) => String.Compare(x, y, true) == 0 }
            };

            bool result = comparer.Equals(value1, value2);
            Assert.AreEqual(expected, result);
        }
 public bool Equals(VariadicOperationExpression other)
 => ReferenceEquals(this, other) ||
 !(other is null) && Operator == other.Operator && StructuralEqualityComparer.Equals(Operands, other.Operands);
        public void Compare_null_enumerations_ignoring_order(Foo value1, Foo value2, bool expected)
        {
            var comparer = new StructuralEqualityComparer<Foo>
            {
                { x => x.Children, new StructuralEqualityComparer<ChildFoo> { x => x.Tag }, StructuralEqualityComparerOptions.IgnoreEnumerableOrder }
            };

            var result = comparer.Equals(value1, value2);

            Assert.AreEqual(expected, result);
        }
        public void Compare_null_references()
        {
            var comparer = new StructuralEqualityComparer<Foo>
            {
                { x => x.Number },
            };

            var foo = new Foo() { Number = 123, Text = "Hello", Duration = TimeSpan.FromMinutes(600) };

            Assert.IsFalse(comparer.Equals(foo, null));
            Assert.IsFalse(comparer.Equals(null, foo));
            Assert.IsTrue(comparer.Equals(null, null));
        }
 /// <summary>
 /// 确定当前结构化对象与指定结构化对象的元素是否对应相等。
 /// </summary>
 /// <param name="value">要进行相等比较的结构化对象。</param>
 /// <param name="other">要与当前结构化对象进行相等比较的结构化对象。</param>
 /// <typeparam name="T">结构化对象的类型。</typeparam>
 /// <returns>若 <paramref name="value"/> 与 <paramref name="other"/> 的每个元素都对应相等,
 /// 则为 <see langword="true"/>;否则为 <see langword="false"/>。</returns>
 public static bool StructuralEquals <T>(this T?value, T?other) =>
 StructuralEqualityComparer.Equals(value, other);