The conversion operator introduces a "substitutability" between classes )". "Replace" means that the instance of one class can be replaced with the instance of another class. This can be a benefit for us: the object of a derived class can be used as a base class object.
For example, in the classic shape class hierarchy, we can create a shape base class and derive many subclasses: rectangle (rectangle), ellipse (elliptic), circle (circle). We can use a circle subclass to replace any shape. Replacement is implemented because of the role of polymorphism, because circle is a more specific shape type. Some conversions automatically work when we create a class. For example, any object can be used as a system. object instance, because system. object is the root class in the. NET class hierarchy. Similarly, any class object can be implicitly used as an interface implemented by it or its base class. In addition, C # supports many numerical conversions.
After defining the conversion operators for our types, we are actually telling the compiler that these types can be used as target types. Such replacement often leads to some strange bugs, because our type may not be the perfect replacement for the target type. For example, the result of changing the target type status may not be reflected in our type. Worse, if our conversion operator returns a temporary object, the effect of the change will be limited to the temporary object, and will be discarded and become a junk object. Finally, the call rule of the conversion operator is based on the object's compile-time type, rather than the runtime type. To this end, type users may need to execute multiple forced transformations to call the conversion operators, which will lead to difficult maintenance.Code.
To convert a type to another type, we should use the constructor. This method clearly reflects the behavior of creating new objects. The conversion operators introduce difficult issues for the code. Suppose we have obtained the code of a Class Library 3-1. Both the circle class and ellipse class are derived from the shape class. We decided to keep the class hierarchy unchanged because although circle is correlated with ellipse, we do not want non-Abstract leaf classes in the class hierarchy; when trying to make the circle class derived from the ellipse class, some implementation problems may occur. However, we always think that every circle can be an ellipse. Some ellipse can also be used as a circle.
This causes us to add two conversion operators. Since every circle is an ellipse, we need to add an implicit conversion operator to convert a circle to an ellipse. When a type needs to be converted to another type for normal operation, the implicit conversion operator is called. In contrast, when weSource codeWhen the forced transformation operator is used, the explicit conversion operator is called. See the following code:
Public Class Circle: Shape
{
Private Pointf _ center;
Private Float _ Radius;
Public Circle ():
This (Pointf. empty, 0 )
{
}
Public Circle (pointf C, Float R)
{
_ Center=C;
_ Radius=R;
}
Public Override Void Draw ()
{
//......
}
Static Public Implicit Operator Ellipse (Circle C)
{
Return NewEllipse (C. _ center, C. _ center,
C. _ radius, C. _ radius );
}
}
With the implicit conversion operator, we can use the circle object wherever ellipse is needed. In addition, such a conversion will automatically occur:
Public Double Computearea (ellipse E)
{
//Returns the area of the ellipse.
}
// Call:
Circle C = New Circle ( New Pointf ( 3.0f , 0 ), 5.0f );
Computearea (C );
The above example shows the so-called "Replacement": Circle is used where ellipse was originally required. After replacement, the computearea () function works well, Which is lucky. However, for the following functions:
Public Void Flatten (ellipse E)
{
E. r1/= 2;
E. r2* = 2;
}
// Use circle to call:
Circle C = New Circle ( New Pointf ( 3.0f , 0 ), 5.0f );
Flatten (C );
There is a problem. The flatten () method accepts an ellipse as the parameter. The compiler must convert the circle to ellipse. This is the work of the implicit conversion operator we created above.
After the implicit conversion operator is called, a temporary ellipse object is created and passed to the flatten () function as a parameter. This temporary ellipse object will be modified by the flatten () function and then become a junk object. The flatten () function only shows some side effects on the temporary ellipse object. The result is that the real circle Object C does not happen.
Changing the implicit conversion operator to an explicit conversion operator only forces the user to add a transformation action:
Circle C = new circle (New pointf (3.0f, 0), 5.0f );
Flatten (ellipse) C );
The original problem was not solved. Forcing a user to add a transformation action causes the same problem-a temporary object is still created, flatten the temporary object, and then discarded, and the circle Object C is not changed at all. However, if we create a constructor to convert the circle to ellipse, the following code is very clear:
Circle C = new circle (New pointf (3.0f, 0), 5.0f );
Flatten (New ellipse (c ));
Vast majorityProgramWhen you see the above two lines of code, you will immediately understand that any changes to ellipse in flattern () will be lost. In this way, the ellipse object will be tracked as follows:
Circle C = New Circle ( New Pointf ( 3.0f , 0 ), 5.0f );
// Process circle.
// ......
// To an ellipse.
Ellipse E = New Ellipse (C );
Flatten (E );
Variable E contains the flattened ellipse. By using constructors instead of conversion operators, we have not lost any functions, but made the creation of new objects clearer. (For those C ++ veterans, note that C # will not use the constructor for implicit or explicit conversion. C # creates a new object only when the new operator is explicitly used. Therefore, the C # constructor does not need to use the explicit it keyword .)
Return the internal fields of the object in the conversion operator. Although the above actions are not involved, they will cause other problems-they will cause serious vulnerabilities to the class encapsulation. Because if we forcibly convert our type to another type, the customer program of the class can implement internal variables of the category class. This practice should be avoided for whatever reason.
To sum up, the "Replacement" obtained by the conversion operator brings some problems to the code. The conversion operator is used to indicate to the class user that the user can use other classes to replace the class. When accessing a replaced object, it is actually a temporary object or an internal field that deals with the client program. After these temporary objects are modified, the results are discarded. This strange bug is hard to find, because the code for type conversion is generated by the compiler. Therefore, we should avoid using conversion operators.