Valid tive C # item3 prefer the is or as operators to casts.
Principle 3: select the is or as operator instead of forced type conversion.
C # is a strong data type language. Good programming practices mean that we should do our best to avoid forced conversion from one data type to another. However, in some cases, runtime type detection is inevitable. In C #, most of the time you need to use the system. Object Type for the parameters that call the function, because framwork has already defined the function prototype for us. You are likely to try to convert those types down to interfaces or classes of other types. You have two options: Use the as operator, or use the old C style for forced conversion. (Either way) You must also protect variables: You can try to use is for conversion, but use as for conversion or force conversion.
At any time, the correct choice is to use the as operator for type conversion. Because it is safer than blindly forced conversion and has a higher running timeliness rate. Not all user-defined types can be converted using the AS and is operators. They can be converted only when the runtime type matches the target type. They will never construct a new object to meet (conversion) requirements.
Let's look at an example. You wrote a Section Code To convert an object instance of any type to a mytype instance. You write the code like this:
Object o = factory. GetObject ();
// Version one:
Mytype T = O as mytype;
If (T! = NULL)
{
// Work with T, it's a mytype.
} Else
{
// Report the failure.
}
Or you can write:
Object o = factory. GetObject ();
// Version two:
Try {
Mytype T;
T = (mytype) O;
If (T! = NULL)
{
// Work with T, it's a mytype.
} Else
{
// Report a null reference failure.
}
} Catch
{
// Report the conversion failure.
}
You will agree that the first method is simpler and easier to read. It does not have a try/catch structure, so you can avoid (performance) overhead and (multiple writes) code at the same time. We noticed that the forced conversion method has to add a structure to detect whether the conversion forces the conversion of a null object. Null can be converted to any reference type, but the as operator returns a null even if it is converted to a null reference. Therefore, when you use forced type conversion, you must use a try/catch structure to capture the exception when converting null. When using as for conversion, you just need to simply check that the converted instance is not null.
The converted object and converted result may both be null. The preceding two types of null are described. Note the difference. Forced conversion is not safe, and exceptions may be thrown. Therefore, the try/catch structure must be used to ensure Program Normal operation, while the as conversion is safe, and no exception is thrown, but after the conversion fails, the result is null)
the biggest difference between forced conversion and as conversion is how to convert user-defined types.
unlike other operations, the AS and is operators must detect the type of the conversion target at runtime. If a specified object is not a required conversion type or is derived from a required conversion type, the conversion will fail. On the other hand, forced conversion can be used to convert an object to the required type. This also includes the conversion of the built-in data (built-in numberic) type. Forcibly converting a long string to a short may cause data loss.
the same problem is also hidden in the conversion of user-defined types. Consider this type:
public class secondtype
{< br> private mytype _ value;
// other details elided
// conversion operator.
// This converts a secondtype to
// A mytype, see item 29.
Public Static Implicit operator mytype (secondtype t)
{< br> return T. _ value;
}< BR >}
assume that the factory started in the code snippet. the GetObject () function returns secondtype data:
Object o = factory. getObject ();
// o is a secondtype:
mytype T = O as mytype; // fails. O is not mytype
If (T! = NULL)
{< br> // work with T, it's a mytype.
}else
{< br> // report the failure.
}< br> // version two:
try {
mytype T1;
T = (mytype) O; // fails. O is not mytype
If (T1! = NULL)
{< br> // work with T1, It's A mytype.
}else
{< br> // report a null reference failure.
}< BR >}catch
{< br> // report the conversion failure.
}< br> both conversions failed. However, I have told you that forced conversion can be completed on user-defined types. You should think that forced conversion will succeed. You are right -- (IF) They will succeed as you think. However, the conversion fails because the code generated by your compiler for object o is based on the compile-time type. For runtime object O, the compiler does not know anything. They are considered as system. obejct type. The compiler believes that the conversion from the system. Object type to the user-type mytype does not exist. It detects the definitions of system. Object and mytype. Without any user-defined type conversion, the compiler (for US) generates code used to detect runtime object O and checks whether the code is of the mytype type. Because the object o is of the secondtype type, it fails. The compiler does not check whether the object o can be converted to the mytype type during actual running.
If you use the following code segment, you can successfully convert from secondtype to mytype:
Object o = factory. GetObject ();
// Version three:
Secondtype ST = O as secondtype;
Try {
Mytype T;
T = (mytype) ST;
If (T! = NULL)
{
// Work with T, it's a mytype.
} Else
{
// Report a null reference failure.
}
} Catch
{
// Report the failure.
}
you should never write a bad code, but it does solve a very common problem. Although you should never write code like this, you can write a function using a system. object parameters to complete the correct conversion:
Object o = factory. getObject ();
dostuffwithobject (o);
private void dostuffwithobject (Object O2)
{< br> try {
mytype T;
T = (mytype) O2; // fails. O is not mytype
If (T! = NULL)
{< br> // work with T, it's a mytype.
}else
{< br> // report a null reference failure.
}< BR >}catch
{< br> // report the conversion failure.
}< BR >}
remember, for a user-defined type object, the conversion operation is only during compilation, not during runtime. There is no relationship between conversions between O2 and mytype at runtime, because the compiler does not know or care about these conversions. Such statements have different behaviors, depending on the statement of the ST type:
T = (mytype) ST;
. Why can the above Code behave differently? What are the different behaviors? The main reason is: The above conversion is during compilation or runtime! If St is a user-defined type, the above conversion is during compilation. The compiler compiles the Il code generated by converting st to the system. Object type. Therefore, an object type cannot be converted to the mytype type during runtime. The solution is to add a statement to convert the object type to secondtype, and then forcibly convert the object type to mytype. However, if St is of the built-in type, its conversion is at runtime. This conversion may be successful, as described later. Therefore, code like this: mytype m_mytype = (m_secondtype as secondtype) as mytype; cannot be compiled, and the error message is that secondtype cannot be converted to mytype during compilation, even if the conversion operator is overwritten .)
However, the following conversions only have one kind of behavior, regardless of the type of St.
Therefore, you should select as to convert objects, rather than force type conversion. In fact, if these types have no relationship with inheritance, but the User-Defined conversion operators exist, the following statement conversion will get a compilation error: t = sT as mytype; now you should understand how to use as much as possible. Next we will discuss how to avoid using. The Value Type of the as operator is invalid. The following Code cannot be compiled:
Object o = factory. getvalue ();
Int I = O as int; // does not compile.
This is because integer data is of the value type and they will never be null. When O is not an integer, what value should I be taken? No matter what value you choose, it will be an invalid integer. Therefore, as cannot be used (on value-type data ). You can insist on using forced conversion:
Object o = factory. getvalue ();
Int I = 0;
Try {
I = (INT) O;
} Catch
{
I = 0;
}
However, you do not have to stick to forced conversion. You can use the is statement to cancel exceptions caused by conversion:
Object o = factory. getvalue ();
Int I = 0;
If (O is int)
I = (INT) O;
Like is and as, it is safe to convert data types. They do not encounter exceptions during conversion at any time. Therefore, you can use is to safely determine the data type. Different from AS, is only performs type detection and returns logical values without conversion .)
If O is another type that can be converted to an integer, such as double, the is operation returns false. For null, is always returns false.
Is should only be used when you cannot use as for conversion.
In addition, this is meaningless redundancy:
// Correct, but redundant:
Object o = factory. GetObject ();
Mytype T = NULL;
If (O is mytype)
T = O as mytype;
If you write the following code, it is redundant like above:
// Correct, but redundant:
Object o = factory. GetObject ();
Mytype T = NULL;
If (O as mytype )! = NULL)
T = O as mytype;
This is both inefficient and redundant. If you use as to convert data, it is unnecessary to use is for detection. You only need to check whether the returned type is null. This is simple.
Now you know the difference between is, As and forced conversion. In the foreach loop, which type of conversion is used?
Public void usecollection (ienumerable thecollection)
{
Foreach (mytype t in thecollection)
T. dostuff ();
}
A foreach loop converts an object to a type available in a loop by force conversion. The above loop code is equivalent to the following handwritten code (hand-coded:
Public void usecollection (ienumerable thecollection)
{
Ienumerator it = thecollection. getenumerator ();
While (it. movenext ())
{
Mytype t = (mytype) It. Current;
T. dostuff ();
}
}
Foreach needs to use forced conversion to support both conversion of the value type and reference type. By selecting forced conversion, the foreach loop can adopt the same behavior without worrying about the type of the target object. Either way, because the foreach loop uses a forced conversion, it may generate badcastexceptions exceptions.
Because ienumberator. Current returns a system. obejct object, this object does not have (overwrite) Conversion operators, so they do not have a (we did above) test.
Note: If you use a set to store secondtype, and you want to use mytype to perform a foreach loop on it, the conversion fails, the reason is that in the loop, the system is used instead of secondtype. object. Therefore, the conversion in the foreach loop is the same as the previous one: mytype t = (mytype) O;. Here the O is secondtype, but it is system. object exists .)
As you know, a set that stores secondtype cannot use loops in the previous function usecollection, which may fail. A forced foreach loop does not check whether objects in the Loop set have valid runtime types during conversion. It only checks whether the system. object returned by ienumerator. current can be converted to the object type used in the loop. In this example, it is of the mytype type.
Finally, sometimes you want to know the exact type of an object, not just to satisfy the currently convertible target type. The as operator returns true if it is converted to any object derived from the target type. The GetType () method gets the runtime object of an object. It provides stricter (type) tests than is and. The type returned by GetType () can be compared with the specified type (to know if it is the type we want ).
Consider this function again:
Public void usecollection (ienumerable thecollection)
{
Foreach (mytype t in thecollection)
T. dostuff ();
}
If you have added a new newtype class derived from mytype, a set of Newtype objects can work well in the usecollection function.
Public class newtype: mytype
{
// Contents elided.
}
If you want to write a function to make it work for all mytype instances, the above method is very good. If you only want to write a function that is only valid for exact mytype objects, you must use exact type comparison. Here, you can complete it within the foreach loop. Most of the time, it is very important to determine the exact type of the object during the runtime, that is, to perform the equality test for the object. In most other comparisons, the. isinst commands provided by is and as are compared in semantics.
Good object-oriented practices tell us that you should avoid type conversion, but sometimes I have no choice. When you cannot avoid conversion, use the is and as operators provided by (C #) language to clearly express your meaning. There are different rules for forced conversions of different methods. In terms of semantics, is and as are correct in most conversions and always succeed when the target object is of the correct type. You should select these basic commands to convert objects. At least they will succeed or fail as expected, rather than select forced type conversion, which will produce some unexpected side effects.
========================================================== ====================
Summary: The third principle of translation.
This afternoon, I downloaded an electronic book on the Internet, so the translation was a little faster. At least I didn't need to enter the code myself. It was very depressing. Continue to translate and try to finish translating all the items during the Spring Festival vacation.
I have basically tested all the things I should pay attention to in the book. Of course, if the problem is clearly correct, I will translate it directly without testing. If you have any questions, try to test them by yourself. Continue to work hard and believe that the translation will be faster and faster.