第4课: 类型转换

经常地,你需要在不同的两个类型之间进行转换. For example, you might need to determine whether an Integer is greater or less than a Double. You might need to pass a Double to a method that requires an Integer as a parameter. Or you might need to display a number as text.

This lesson describes how to convert between types in both Visual Basic and C#. Type conversion is one of the few areas where Visual Basic and C# differ considerably.

Visual Basic 和 C# 的转换

默认情况下, Visual Basic 允许类型之间隐式转换, 但C# 禁止隐私转换,因为那样会导致丢失精度. To turn off implicit conversions in Visual Basic, add Option Strict On to the top of each code file, or (in Visual Studio) select Project, choose Properties, select Compile, and select Option Strict On for the entire project.

如果目标类型能够适应原类型的所有值,那么Visual Basic 和 C#都允许他们隐式转换. 这个被叫做 widening conversion,如下所示:

' VB
Dim i As Integer = 1
Dim d As Double = 1.0001
d = i ' Conversion allowed

// C#
int i = 1;
double d = 1.0001;
d = i; // Conversion allowed.

如果原类型的范围或者精度超过了目标类型, 这个操作叫做 narrowing conversion, 通常需要显示转换. Table 1-7 列出了显示转换的方法:

Table 1-7: Methods for Explicit Conversion

System Type

Visual Basic

C#

Converts

System.Convert

   

Between types that implement the System.IConvertible interface.

 

CType

(type) cast operator

Between types that define conversion operators.

type.ToString, type.Parse

   

Between string and base types; throws exception if the conversion is not possible.

type.TryParse, type.TryParseExact

   

From string to a base type; returns false if the conversion is not possible.

 

CBool, CInt, CStr, etc.

 

Between base Visual Basic types; compiled inline for better performance. (Visual Basic only.)

 

DirectCast, TryCast

 

Between types. DirectCast throws an exception if the types are not related through inheritance or if they do not share a common interface; TryCast returns Nothing in those situations. (Visual Basic only.)

.NET 2.0 

TryParse, TryParseExact, and TryCast are new in .NET 2.0. Previously, you had to attempt a parsing or conversion and then catch the exception if it failed.

显示转换有可能会失败,所以你需要使用 Try blocks 或 TryCast  或 TryParse,并且检查返回值.

什么是装箱和拆箱?

装箱把一个值类型转换成引用类型,拆箱正好相反. 下面的例子说明装箱操作把 Integer (一个值类型) 转换成 Object (一个引用类型):

'VB
Dim i As Integer = 123
Dim o As Object = CType(i, Object)

// C#
int i = 123;
object o = (object) i;

Unboxing occurs if you assign a reference object to a value type. The following example demonstrates unboxing:

'VB
Dim o As Object = 123
Dim i As Integer = CType(o, Integer)

// C#
object o = 123;
int i = (int) o;

Best Practices—Boxing and unboxing 

Boxing and unboxing incur overhead, so you should avoid them when programming intensely repetitive tasks. Boxing also occurs when you call virtual methods that a structure inherits from System.Object, such as ToString. Follow these tips to avoid unnecessary boxing:

  • Implement type-specific versions (overloads) for procedures that accept various value types. It is better practice to create several overloaded procedures than one that accepts an Object argument.

  • Use generics whenever possible instead of accepting Object arguments.

  • Override the ToString, Equals, and GetHash virtual members when defining structures.

如何实现自定义类型转换?

You can define conversions for your own types in several ways. Which technique you choose depends on the type of conversion you want to perform:

  • Define conversion operators to simplify narrowing and widening conversions between numeric types.

  • Override ToString to provide conversion to strings, and override Parse to provide conversion from strings.

  • Implement System.IConvertible to enable conversion through System.Convert. Use this technique to enable culture-specific conversions.

  • Implement a TypeConverter class to enable design-time conversion for use in the Visual Studio Properties window. Design-time conversion is outside the scope of the exam and the TypeConverter class is not covered in this book.

More Info—Design-time conversion 

For more information about design-time conversion, read "Extending Design-Time Support" at http://msdn2.microsoft.com/en-us/library/37899azc(en-US,VS.80).aspx.

.NET 2.0 

Conversion operators are new in .NET 2.0.

通过定义operators操作,允许你直接把一个值类型转换成自定义类型. Use the Widening/implicit keyword for conversions that don't lose precision; use the Narrowing/explicit keyword for conversions that could lose precision. For example, the following structure defines operators that allow assignment to and from integer values:

' VB
Structure TypeA
Public Value As Integer

' Allows implicit conversion from an integer.
Public Shared Widening Operator CType(ByVal arg As Integer) As TypeA
Dim res As New TypeA
res.Value = arg
Return res
End Operator

' Allows explicit conversion to an integer
Public Shared Narrowing Operator CType(ByVal arg As TypeA) As Integer
Return arg.Value
End Operator

' Provides string conversion (avoids boxing).
Public Overrides Function ToString() As String
Return Me.Value.ToString
End Function

End Structure

// C#
struct TypeA

{
public int Value;

// Allows implicit conversion from an integer.
public static implicit operator TypeA(int arg)
{
TypeA res = new TypeA();
res.Value = arg;
return res;
}

// Allows explicit conversion to an integer
public static explicit operator int(TypeA arg)
{
return arg.Value;
}

// Provides string conversion (avoids boxing).
public override string ToString()
{
return this.Value.ToString();
}
}

The preceding type also overrides ToString to perform the string conversion without boxing. Now you can assign integers to the type directly, as shown here:

' VB
Dim a As TypeA, i As Integer
' Widening conversion is OK implicit.
a = 42 ' Rather than a.Value = 42
' Narrowing conversion must be explicit.
i = CInt(a) ' Rather than i = a.Value
' This syntax is OK, too.
i = CType(a, Integer)
Console.WriteLine("a = {0}, i = {0}", a.ToString, i.ToString)

// C#
TypeA a; int i;
// Widening conversion is OK implicit.
a = 42; // Rather than a.Value = 42
// Narrowing conversion must be explicit.
i = (int)a; // Rather than i = a.Value
Console.WriteLine("a = {0}, i = {0}", a.ToString(), i.ToString());

To implement the System.IConvertible interface, add the IConvertible interface to the type definition. Then use Visual Studio to automatically implement the interface. Visual Studio inserts member declarations for 17 methods, including GetTypeCode, ChangeType, and ToType methods for each base type. You don't have to implement every method, and some-such as ToDateTime-will probably be invalid. For invalid methods, simply throw an exception-Visual Studio automatically adds code to throw an exception for any conversion methods you don't implement.

After you implement IConvertible, the custom type can be converted using the standard System.Convert class as shown here:

' VB
Dim a As TypeA, b As Boolean
a = 42
' Convert using ToBoolean.
b = Convert.ToBoolean(a)
Console.WriteLine("a = {0}, b = {1}", a.ToString, b.ToString)

// C#
TypeA a; bool b;
a = 42;
// Convert using ToBoolean.
b = Convert.ToBoolean(a);
Console.WriteLine("a = {0}, b = {1}", a.ToString(), b.ToString());

Lab: Safely Performing Conversions 安全执行转换

The following exercises show how to avoid problems with implicit conversions so that your programs function predictably. If you encounter a problem completing an exercise, the completed projects are available on the companion CD in the Code folder.

Exercise 1: Examine Implicit Conversion
Image from book

In this exercise, you will examine conversion to determine which number types allow implicit conversion.

  1. Create a new console application in Visual Studio.

  2. Declare instances of three value types: Int16, Int32, and double. The following code sample demonstrates this:

    ' VB
    Dim i16 As Int16 = 1
    Dim i32 As Int32 = 1
    Dim db As Decimal = 1

    // C#
    Int16 i16 = 1;
    Int32 i32 = 1;
    double db = 1;
  3. Attempt to assign each variable to all the others, as the following code sample demonstrates.

    ' VB
    i16 = i32
    i16 = db

    i32 = i16
    i32 = db

    db = i16
    db = i32

    // C#
    i16 = i32;
    i16 = db;

    i32 = i16;
    i32 = db;

    db = i16;
    db = i32;
  4. Attempt to build your project. Which implicit conversions did the compiler allow, and why?

Image from book
Exercise 2: Enable Option Strict (Visual Basic Only)
Image from book

In this exercise, which is only for developers using Visual Basic, you will modify the compiler's options and then rebuild the project you created in Exercise 1.

  1. In Visual Studio, open the project you created in Exercise 1.

  2. Click the Project menu, and then click ProjectName Properties.

  3. Click the Compile tab. For Implicit Conversion, change the Notification type to Error.

  4. Attempt to build your project. Which implicit conversions did the compiler allow, and why?

Image from book

Lesson Summary

  • The .NET Framework can automatically convert between built-in types. Widening conversions occur implicitly in both Visual Basic and C#. Narrowing conversions require explicit conversion in C#, while Visual Basic allows narrowing conversions by default.

  • Boxing allows any type to be treated as a reference type.

  • You must specifically implement conversion operators to enable conversion in custom types.

Lesson Review

You can use the following questions to test your knowledge of the information in Lesson 4, "Converting Between Types." The questions are also available on the companion CD if you prefer to review them in electronic form.

Answers 

Answers to these questions and explanations of why each answer choice is right or wrong are located in the "Answers" section at the end of the book.

1. 

Why should boxing be avoided? (Choose one.)

  1. It adds overhead.

  2. Users must have administrative privileges to run the application.

  3. It makes code less readable.

Image from book

2. 

Structures inherit ToString from System.Object. Why would someone override that method within a structure? (Choose as many correct answers as apply.)

  1. To avoid boxing.

  2. To return something other than the type name.

  3. The compiler requires structures to override the ToString method.

  4. To avoid run-time errors caused by invalid string conversions.

Image from book

3. 

If there is no valid conversion between two types, what should you do when implementing the IConvertible interface?

  1. Delete the ToType member that performs the conversion.

  2. Throw an InvalidCastException.

  3. Throw a new custom exception reporting the error.

  4. Leave the member body empty.

Image from book

4. 

With strict conversions enabled, which of the following would allow an implicit conversion? (Choose all that apply.)

  1. Int16 to Int32

  2. Int32 to Int16

  3. Int16 to Double

  4. Double to Int16

Image from book

Answers

1. 

Correct Answer: A

  1. Correct: The primary reason to avoid boxing is because it adds overhead.

  2. Incorrect: Boxing requires no special privileges.

  3. Incorrect: Boxing does not make code less readable.

2. 

Correct Answers: A and B

  1. Correct: Value types are boxed when an abstract method inherited from System.Object is called. Overriding the method avoids boxing.

  2. Correct: By default, the ToString method simply returns the type name, which is typically not useful for a consuming application.

  3. Incorrect: The compiler does not require structures to override the ToString method.

  4. Incorrect: ToString never causes a run-time error; it simply returns the type name unless overridden.

3. 

Correct Answer: B

  1. Incorrect: You can't omit a member of an interface and still conform to that interface.

  2. Correct: InvalidCastException is the recommended exception to throw.

  3. Incorrect: While you could throw a custom exception, using standard exception types makes it easier for developers writing code to consume your type to catch specific exceptions.

  4. Incorrect: You must return a value for each conversion member.

4. 

Correct Answers: A and C

  1. Correct: You can convert from Int16 to Int32 because that is considered a widening conversion. Because Int32 can store any value of Int16, implicit conversion is allowed.

  2. Incorrect: You cannot convert from Int32 to Int16 because that is considered a narrowing conversion. Because Int16 cannot store any value of Int32, implicit conversion is not allowed.

  3. Correct: You can convert from Int16 to Double because that is considered a widening conversion. Because Double can store any value of Int16, implicit conversion is allowed.

  4. Incorrect: You cannot convert from Double to Int16 because that is considered a narrowing conversion. Because Int16 cannot store any value of Double, implicit conversion is not allowed.

posted @ 2008-09-19 11:32  无尽思绪  阅读(468)  评论(0编辑  收藏  举报