Analyzing the equivalent C # 1

Brief introduction

Recent looking at "C # in a nutshell" book, you can see that although the .NET Framework has some drawbacks and shortcomings, but on the whole the design is quite good. Here, we intend to compare between the two objects related to expound from the C # language.

Equal comparison value and reference type

In C #, we know that for different types of data, comparing different ways. The most typical is, the type of comparative value is the value of both are equal, but is more of a reference type is whether both refer to the same object. The following example you can see the difference between their two.

int v1 = 3, v2 = 3;
object r1 = v1;
object r2 = v1;
object r3 = r1;
Console.WriteLine($"v1 is equal to v2: {v1 == v2}");    // true
Console.WriteLine($"r1 is equal to r2: {r1 == r2}");    // false
Console.WriteLine($"r1 is equal to r3: {r1 == r3}");    // true

In this example, the type intbelongs to the type of value, its variables v1, and v2are 3. You can see from the results output, the two really are equal. But for objectthis reference types, even with a intconversion from the type of data (the intboxed data type), both of which are not the same references and therefore are not equal (i.e., row 6). But for r3it, are references to r1objects referred to, and thus r3and r1equal.

Although the value of the type Comparison by value, compared with a reference type data according to whether the reference. However, there are some special circumstances. A typical example is the string string, and System.Uri. Although these two types of data type is a reference type (class essentially all), but the results on which has demonstrated equivalence determination value type and the like.

string s1 = "test";
string s2 = "test";
Uri u1 = new Uri("https://www.bing.com");
Uri u2 = new Uri("https://www.bing.com");
Console.WriteLine($"s1 is equal to s2: {s1 == s2}");    // true
Console.WriteLine($"u1 is equal to u2: {u1 == u2}");    // true

We can see, these two data types to break the rules given before. Although stringand System.Uricompare the results of two classes are similar, but the two concrete realization of behavior is not the same. Then the different data types more specific process is how, and how to customize comparative embodiment will be discussed in a subsequent section. However, we first look at how to deal with equal logic in C #.

Equality comparison function and associated

In the C # language system, you can know the class Objectof all data types throughout the root class. From the .NET Core 3.0 Objectcan be seen, the function associated with the determination of equivalence has four, two of which members of the class methods, static member 2 is a method, as follows:

public virtual bool Equals(object? obj);
public virtual int GetHashCode();
public static bool ReferenceEquals(object? objA, object? objB);
public static bool Equals(object? objA, object? objB);

It may be noted that this and other information which is not exactly the same, the only difference is that the parameter type is passed object?instead object. This is mainly introduced in C # 8.0 version can be null reference type. Here it can be a null reference types are not the focus of this article, as here can be objectto deal with.

Here we are introduced one by one these four functions:

  1. Class member method Equals. The role of this method is that the object that is currently used and the incoming object to be compared, if agreement is considered to be equal. This method is set virtual, i.e., in a subclass can override this method.
  2. Class member method GetHashCode. This method is mainly used in the hash process, such as hash tables and dictionaries classes. For this function, it is a basic requirement, if two objects are deemed equal, they will return the same hash value. For different objects, the function does not require to be returned must different hash values, but want to return to a different hash values as much as possible, to be able to distinguish between different data objects in the hash processing. The method as above, because virtualthe keyword modification, can also be overridden in a subclass.
  3. Static member method ReferenceEquals. This method is mainly used to determine whether two objects point to the same reference. In the source code can also be seen in its essence it is saying: return objA == objB;. Since this method is static and can not be rewritten.
  4. Static member method Equals. For this method, can also be seen from the source, the same is first determined whether the two references in not the same, then the object using the method Equalsdetermines whether both are equal. Similarly, because the method is static, but also can not be rewritten.

stringAnd System.Uriequivalence comparison

Well, we come back to the original question, why stringand System.Uriperformance behavior and other reference types are not the same, but similar type and value. In fact, strictly speaking, stringand System.Urithe object comparison, although the performance on similar value types, but the details inside the two are not the same.

For stringit is, in most cases, in which a copy of the program, a string will only be stored once, no matter how many new string variable, as long as its value is the same, then all references to the same memory address. So for string comparisons, which is still relatively quoted, but most of the same value is a reference to the same object.

The System.Uridifferent classes for such objects, how many objects will be built on the heap corresponding number of open space and memory to store data. However, in comparison, a first comparison reference comparison method used then the comparison value. That is, when not both of the references to the same object and then compare its value is equal to the ( source ).

string s1 = "test";
string s2 = "test";
Uri u1 = new Uri("https://www.bing.com");
Uri u2 = new Uri("https://www.bing.com");
Console.WriteLine($"s1 is equal to s2 by the reference: {Object.ReferenceEquals(s1, s2)}"); // true
Console.WriteLine($"s1 is equal to s2: {s1 == s2}");    // true
Console.WriteLine($"u1 is equal to u2 by the reference: {Object.ReferenceEquals(u1, u2)}"); // false
Console.WriteLine($"u1 is equal to u2: {u1 == u2}");    // true

Examples of the above can be seen that the two string variables point to the same data object ( ReferenceEqualsthe method is to determine whether two references refer to the same object, where you can see the return value true). For System.Uri, the two variables do not point to the same object, but the subsequent determination of both equal still equal, this time can be seen at this time according to the value to determine whether both are equal.

Generic Interface IEquatable<T>

It can be seen from the above examples, C # whether the two objects by a substantially equal Equalsmethod to determine. However, the Equalsmethod is also not a panacea, which is particularly reflected in the value of the type which.

Since the Equalsmethod requires the parameters passed type object. If the method is applied to a value of type, it will cause the value of the type of cast to objectthe type, i.e. will packing (Boxing) once. Boxing and unboxing is generally more time-consuming and tends to decrease the efficiency. In addition, objectthe type of the object means that the class can be equal judgment and any other class objects, but in general, we determine whether an object is equal to the premise of the two objects are certainly the same class.

C # solution adopted is to use a generic interface IEquatable<T>to solve. IEquatable<T>It comprises two main methods, as follows:

public interface IEquatable<T>
{
    bool Equals(T other);
}

And Object.Equals(object? obj)compared to the interior of a generic function method, if a data structure or class implements the interface and the like, then when the call Equalswhen the method according to the principles of the most suitable type, it first calls IEquatable<T>in the Equals(T other)method. This avoids value type packing operation.

Custom comparison method

In some cases, in order to better simulate real-world scenarios, we need to customize the comparison between two individuals. In order to achieve such a comparison method, there are usually three steps need to be completed:

  1. Rewriting Equals(object obj)and GetHashCode()methods;
  2. Overload operator ==and !=;
  3. Realization IEquatable<T>method;

On the first point, these two functions must be rewritten. For Equals(object obj)the true, if realized within the generic interface method can be considered where the method can be called directly. GetHashCode()As for distinguishing different objects, so that if two objects are equal, then the hash value which should be equal, this will have a better performance in the hash table and the dictionary class.

For the second and third points, it is not necessary, but in general, in order to better use, both of which need to be the best heavy-duty.

You can see, these three points are related to the logical comparison. In general, we tend to compare the core logic in a generic interface for other methods, you can by calling the method in a generic interface.

For example

Here, we take a small example. Imagine this scenario, the current machine learning more and more fiery, but can not do without talking about machine learning matrix operations. For the matrix, we can use a two-dimensional array to hold. In the field of mathematics, we determine whether two matrices are equal, is to determine each element in the two matrices are equal, that is, the value of the type of judgment way. In C #, due to the two-dimensional array is a reference type, you can not directly use the equivalence determination to achieve this goal. Therefore, we need to modify their way of judgment.

   public class Matrix : IEquatable<Matrix>
    {
        private double[,] matrix;

        public Matrix(double[,] m)
        {
            matrix = m;
        }

        public bool Equals([AllowNull] Matrix other)
        {
            if (Object.ReferenceEquals(other, null))
                return false;
            if (matrix == other.matrix)
                return true;
            if (matrix.GetLength(0) != other.matrix.GetLength(0) ||
                matrix.GetLength(1) != other.matrix.GetLength(1))
                return false;
            for (int row = 0; row < matrix.GetLength(0); row++)
                for (int col = 0; col < matrix.GetLength(1); col++)
                    if (matrix[row,col] != other.matrix[row,col])
                        return false;
            return true;
        }

        public override bool Equals(object obj)
        {
            if (!(obj is Matrix)) return false;
            return Equals((Matrix)obj);
        }

        public override int GetHashCode()
        {
            int hashcode = 0;
            for (int row = 0; row < matrix.GetLength(0); row++)
                for (int col = 0; col < matrix.GetLength(1); col++)
                    hashcode = (hashcode + matrix[row, col].GetHashCode()) % int.MaxValue;
                return hashcode;
        }

        public static bool operator == (Matrix m1, Matrix m2)
        {
            return Object.ReferenceEquals(m1, null) ? Object.ReferenceEquals(m2, null) : m1.Equals(m2);

        }
        public static bool operator !=(Matrix m1, Matrix m2)
        {
            return !(m1 == m2);

        }
    }
    
Matrix m1 = new Matrix(new double[,] { { 1, 2, 3 }, { 4, 5, 6 } });
Matrix m2 = new Matrix(new double[,] { { 1, 2, 3 }, { 4, 5, 6 } });

Console.WriteLine($"m1 is equal to m2 by the reference: {Object.ReferenceEquals(m1, m2)}");     // false
Console.WriteLine($"m1 is equal to m2: {m1 == m2}");    //true

Comparison logic implemented in Equals(Matrix other)the. In this method, the matrix is first determined whether the two references with a two-dimensional array, after determining the number of ranks are equal, then the final determination for each element. The entire core logic here. For the Equals(object obj)well ==and !=the direct call Equals(Matrix other)method. Note that, in overloaded ==when the symbol can not be directly used m1==nullto determine whether the first object is empty, otherwise an infinite loop that calls the ==operator overloading function. In this function requires determination of the need for a reference, you can use Objectclass static method ReferenceEqualsto determine.

to sum up

Overall, equal comparison in C # is a reference to the rule: the value is a value type of comparison is equal, the comparison of the reference type is whether both reference the same object. In addition, this article describes some of the related functions and an equality and interfaces, functions and effects of these interfaces is to build a framework of the equality comparison. These functions and interfaces, not only can use the default comparison rules, but we can also customize compare rules. In the last article, we give an example to simulate a custom comparison rules of use. By this example, we can clearly see the custom implementation compare.

Guess you like

Origin www.cnblogs.com/iskcal/p/csharp-equal-compare-1.html