Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C# ПІДРУЧНИКИ / c# / MS Press - Msdn Training Programming Net Framework With C#

.pdf
Скачиваний:
173
Добавлен:
12.02.2016
Размер:
16.87 Mб
Скачать

Module 6: Working with Types

19

 

 

 

Conversions

Topic Objective

To explain when and how to use explicit and implicit conversions.

Lead-in

Conversion is the process of changing a type to another type.

! Explicit Conversions

int x = 5; int x = 5;

double y = (double) x; double y = (double) x;

! Implicit Conversions

int x = 5; int x = 5; double y = x; double y = x;

! Conversion Operators

public static implicit operator byte(Digit d) public static implicit operator byte(Digit d) public static explicit operator Digit(byte b) public static explicit operator Digit(byte b)

*****************************ILLEGAL FOR NON-TRAINER USE******************************

Conversion is the process of changing a type to another type. Conversions are necessary when a value of one type must be assigned to a variable of a different type during assignment or when passing arguments to a method call.

Explicit and Implicit Conversions

In explicit conversions, you do not need to specify the name of the type in a cast for assignment conversions or operand conversions.

The following example shows how to convert an int value to a double value by using an explicit conversion.

public static bool BiggerThanFive(double value)

{

if (value > 5) return true; else return false;

}

public static void Main()

{

int x = 5;

//Use an explicit conversion for assignment double y = (double) x;

//Use an explicit conversion for operand bool answer = BiggerThanFive((double) x);

}

The preceding example is an example of a widening conversion. The bit size of an int is 32 bits, and the bit size of a double is 64 bits. Therefore, the type was widened during the conversion.

20

Module 6: Working with Types

In implicit conversions, you do not need to specify the name of the type during the conversion. Widening conversions can occur implicitly, and so they allow for simpler syntax. The preceding example could be written more simply as follows:

public static void Main()

{

int x = 5;

//Use an implicit conversion for assignment double y = x;

//Use an implicit conversion for operand bool answer = BiggerThanFive(x);

}

The following table shows the allowable implicit conversions for numeric types in the common language runtime.

From

To

 

 

sbyte

short, int, long, float, double, or decimal

byte

short , ushort, int, uint, long, ulong, float, double, or decimal

short

int, long, float, double, or decimal

ushort

int, uint, long, ulong, float, double, or decimal

int

long, float, double, or decimal

uint

long, ulong, float, double, or decimal

long

float, double, or decimal

char

ushort, int, uint, long, ulong, float, double, or decimal

float

double

ulong

float, double, or decimal

Some conversions can result in a loss of precision. For example, converting an int to a float is an allowable implicit conversion, but a float has only seven digits of precision. Depending on the value of the int, some digits of precision may be lost.

The following example shows a narrowing conversion in which a double is converted to an integer.

double y = 4.56; int x = (int) y;

In this case, narrowing conversions must be explicit because there is almost always a loss of precision in the value that is being converted. In the preceding example, the value 4.56 will be truncated to 4 when it is assigned to x.

Module 6: Working with Types

21

 

 

 

Conversion Operators

You can create user-defined conversions by providing conversion operators in your class or struct.

Use the following syntax for specifying a conversion operator:

public static [implicit | explicit] operator conv-type-out (conv-type-in operand)

in which:

!conv-type-out is the name of the type to convert to.

!conv-type-in is the name of the type to convert from.

!operand is the name of the parameter holding the value being converted.

The following example shows how a structure that is called Digit, which represents a value from 0 to 9, can convert implicitly to a byte and explicitly to a Digit.

struct Digit

{

byte value;

public Digit(byte value)

{

if (value < 0 || value > 9) throw new ArgumentException();

this.value = value;

}

public static implicit operator byte(Digit d)

{

//Implicitly convert from Digit to short return d.value;

}

public static explicit operator Digit(byte b)

{

//Explicitly convert from short to Digit return new Digit(b);

}

}

class MainClass

{

public static void Main()

{

Digit dig = new Digit(3);

byte b = dig; //Implicit conversion operator invoked Console.WriteLine(b); //Prints 3

Digit dig2 = (Digit) b; //Explicit conversion invoked Console.WriteLine(dig2); //Implicit conversion prints 3

}

}

22

Module 6: Working with Types

In the preceding example, the conversion from Digit to byte is implicit, while the conversion from byte to Digit is explicit. You should use the following guidelines to determine whether a conversion operator should be implicit or explicit.

!A conversion operator should be implicit to make code easier to read. Implicit conversions should never throw an error.

!A conversion operator should be explicit whenever information could be lost in the conversion or if the conversion could throw an exception.

Module 6: Working with Types

23

 

 

 

Casting

Topic Objective

To explain how casting is used in the .NET Framework.

Lead-in

Casting is used for explicit conversions. However, casting is also used for changing the type that is used to reference an object.

!Casting Up from Derived Class to Base Class

!Casting Down from Base Class to Derived Class

!Type Operators

#is

#as

#typeof

!Casting Interfaces

*****************************ILLEGAL FOR NON-TRAINER USE******************************

Casting is used for explicit conversions. However, casting is also used for changing the type that is used to reference an object.

Casting in an Inheritance Hierarchy

Casting on objects is frequently used to change the reference type of an object to a base class reference or a derived class reference. When casting a derived class to a base class, you do not need to explicitly cast the object. The following example shows how a Shape type reference can be used to work with a Square object.

//Shape is a base class class Shape {...}

//Square is a derived class class Square : Shape {...}

Square sq = new Square(); Shape sh = sq; sh.ShapeMethod();

When casting from a base class to a derived class, you must explicitly cast the object. If you cast to a derived type that does not match the underlying object, an InvalidCastException error will be thrown.

24

Module 6: Working with Types

The following example shows how to cast from a base class type to a derived class type.

Square sq = new Square(); Shape sh = sq;

Square sq2 = (Square) sh;//Cast down to Square sh = new Shape();

sq2 = (Square) sh;//InvalidCastException

Type Operators

C# provides several operators to help cast object types appropriately. These operators help you avoid InvalidCastException errors.

The is operator compares an object to a type. If the type matches the object, true is returned. If the type does not match the object, false is returned.

Shape sh = new Shape(); Square sq;

if (sh is Square) sq = (Square) sh;

The as operator attempts to convert an object to a type. If the conversion is successful, a reference of the specified type is assigned. Otherwise, null is assigned.

Shape sh = new Shape(); Square sq = sh as Square; if (sq == null)

Console.WriteLine("Shape was not a square"); else

Console.WriteLine("Shape was a square");

You can use the typeof operator for more advanced type operations. The typeof operator will return a System.Type instance that describes the type through reflection. If you have an instance of a class, you can call GetType to obtain the same information.

For more information about reflection, the mechanism for obtaining information about loaded assemblies and the types defined within them, including classes, interfaces, and value types, see “Discovering Type Information at Run time,” in the .NET Framework Software Developer’s Kit (SDK) documentation and Module 17, “Attributes,” in Course 2349B,

Programming with the Microsoft .NET Framework (Microsoft Visual C#

.NET).

Casting Interfaces

Casting is also used to obtain interface references to an object. The following example shows how the interface ICDPlayer can be obtained through casting from the Device class, which implements the ICDPlayer interface.

ICDPlayer player;

Device d = new Device();

player = (ICDPlayer) d;//Cast to interface player.Play();

Module 6: Working with Types

25

 

 

 

Boxing

Topic Objective

To explain when and how boxing occurs.

Lead-in

Boxing and unboxing are conversions that occur automatically when a value type is converted to an object or when an object is converted to a value type.

!Boxing Occurs to Convert a Value Type to a Reference Type

#Instance of System.Object is allocated on heap

#Value type is copied to new object

int x = 5; //Value type int x = 5; //Value type Object o = x; //Boxed Object o = x; //Boxed

Console.WriteLine("The answer is : {0} ", x);//Boxed Console.WriteLine("The answer is : {0} ", x);//Boxed

! Unboxing Occurs to Retrieve Value Type from Object

int y = (int) o; //unbox int y = (int) o; //unbox

! Boxing Can Be Expensive If Inside Loops

*****************************ILLEGAL FOR NON-TRAINER USE******************************

Boxing and unboxing are conversions that occur automatically when a value type is converted to an object or when an object is converted to a value type. Boxing and unboxing occur implicitly and can affect the performance of your application code.

When Boxing Occurs

Boxing occurs when a value type must be converted to a reference type. Boxing only occurs when converting to the type System.Object. Any other reference type must have implicit or explicit conversion operators to convert from value types.

The following example shows how an integer value type is converted to an object reference type.

int x = 5; //Value type

Object o = x; //Implicit boxing operation occurs

In the preceding example, a boxing operation occurs when the object o is assigned the value in x.

Boxing frequently occurs when calling methods that accept parameters of type Object. For example, Console.WriteLine can accept a string and an object parameter as follows.

int x = 5;

Console.WriteLine("The answer is: {0}",x);//Boxing occurs

In the preceding example, a boxing operation occurs on the call to

Console.WriteLine. Because Console.WriteLine requires an Object type as the second parameter, x is implicitly boxed as an Object type.

26

Module 6: Working with Types

You can determine where boxing operations occur in your code by examining the disassembly. For example, if you use the Microsoft intermediate language (MSIL) Disassembler (Ildasm.exe), you will see an operation code called box, which is followed by the type that is being boxed.

How Boxing Works

A value type must be copied from the stack to the heap if the value is going to be treated as a reference type. The boxing operation allocates memory on the heap for the value type and creates a reference to the new memory location.

Then it copies the value type into the new memory location.

Unboxing

To work with the value inside a boxed type, it must first be unboxed. Unboxing a type will copy it from the heap into a variable on the stack.

The following example shows how to unbox an integer.

int x = 5;

Object o = x; //box

int y = (int) o; //unbox

If the underlying value type that is being unboxed is incompatible with the type of the variable that it is assigned to, you will get an InvalidCastException.

int x = 5; short s;

Object o = x; //box

s = (short) o; //Will throw InvalidCastException

Consequences

Boxing can cause performance problems if you do not minimize its occurrence. The following example shows the consequences of boxing.

int age = 5; int count = 1;

for (count = 1;count < 10;count++)

{

Console.WriteLine("Current Age = {0}",age);//1 Box Console.WriteLine("Age in {0} year(s) will be {1}",

count, age+count);//2 box operations

}

In the preceding example, three boxing operations will occur for every iteration through the loop. This kind of scenario can be very costly in terms of performance. One solution is to box value types before entering a loop, as in the following example.

int age = 5;

Object oAge = age; //1 box operation int count = 1;

for (count = 1;count < 10;count++)

{

Console.WriteLine("Current Age = {0}",oAge); Console.WriteLine("Age in {0} year(s) will be {1}",

count, age+count);//2 box operations

}

Module 6: Working with Types

27

 

 

 

In cases when the need for a higher level of performance is necessary, custom classes can be created to encapsulate values and work with values as reference types.

class RefInt32

{

public int Value;

public RefInt32(int value)

{

this.Value = value;

}

public override string ToString()

{

return Value.ToString();

}

public static implicit operator int (RefInt32 refInt)

{

return refInt.Value;

}

}

int age = 5;

RefInt32 refAge = new RefInt32(age); //Convert to ref type int count = 1;

RefInt32 refCount = new RefInt32(count); //Convert to ref type RefInt32 refTotal = new RefInt32(0);

for (refCount.Value = 1;refCount.Value < 10;refCount.Value++)

{

refTotal.Value = refAge.Value + refCount.Value; Console.WriteLine("Current Age = {0}",refAge); Console.WriteLine("Age in {0} year(s) will be {1}",

refCount,refTotal);//0 box operations

}

The preceding example demonstrates the importance of overriding appropriate Object methods to get expected behavior. If the recipient of your reference type calls one of the methods that are expecting appropriate behavior, it may cause problems. In the preceding example, you must override ToString.

28

Module 6: Working with Types

" Interfaces

Topic Objective

To provide an overview of the topics covered in this section.

Lead-in

When designing and using interfaces, you should consider a number of factors.

!Inheritance Considerations

!Explicit Interface Implementation

*****************************ILLEGAL FOR NON-TRAINER USE******************************

When designing and using interfaces, you should consider a number of factors. This section discusses how multiple inheritance works through interfaces. This section also explains how to explicitly implement interfaces.

Соседние файлы в папке c#