Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# 2008 Step by Step.pdf
Скачиваний:
26
Добавлен:
25.03.2016
Размер:
13.96 Mб
Скачать

406

Part III Creating Components

The completed operator should look exactly as shown in bold here:

struct Minute

{

...

public static bool operator!=(Minute lhs, int rhs)

{

return lhs.value != rhs;

}

...

private int value;

}

10.On the Build menu, click Build Solution. This time, the project builds without errors.

11.On the Debug menu, click Start Without Debugging.

The application runs and displays a digital clock that updates itself every second.

12.Close the application, and return to the Visual Studio 2008 programming environment.

Understanding Conversion Operators

Sometimes it is necessary to convert an expression of one type to another. For example, the following method is declared with a single double parameter:

class Example

{

public static void MyDoubleMethod(double parameter)

{

...

}

}

You might reasonably expect that only values of type double could be used as arguments when calling MyDoubleMethod, but this is not so. The C# compiler also allows MyDoubleMethod to be called with an argument whose type is not double, but only as long as that value can be converted to a double. The compiler will generate code that performs

this conversion when the method is called.

Providing Built-In Conversions

The built-in types have some built-in conversions. For example, an int can be implicitly converted to a double. An implicit conversion requires no special syntax and never throws an exception:

Example.MyDoubleMethod(42); // implicit int-to-double conversion

Chapter 21 Operator Overloading

407

An implicit conversion is sometimes called a widening conversion, as the result is wider than the original value—it contains at least as much information as the original value, and nothing is lost.

On the other hand, a double cannot be implicitly converted to an int:

class Example

{

public static void MyIntMethod(int parameter)

{

...

}

}

...

Example.MyIntMethod(42.0); // compile-time error

Converting from a double to an int runs the risk of losing information, so it will not be done automatically. (Consider what would happen if the argument to MyIntMethod were 42.5— how should this be converted?) A double can be converted to an int, but the conversion re-

quires an explicit notation (a cast):

Example.MyIntMethod((int)42.0);

An explicit conversion is sometimes called a narrowing conversion, as the result is narrower than the original value (it can contain less information) and can throw an OverflowException.

C# allows you to provide conversion operators for your own user-defined types to control whether it is sensible to convert values to other types and whether these conversions are implicit or explicit.

Implementing User-Defined Conversion Operators

The syntax for declaring a user-defined conversion operator is similar to that for declaring an overloaded operator. A conversion operator must be public and must also be static. Here’s a

conversion operator that allows an Hour object to be implicitly converted to an int:

struct Hour

{

...

public static implicit operator int (Hour from)

{

return this.value;

}

private int value;

}

The type you are converting from is declared as the single parameter (in this case, Hour), and the type you are converting to is declared as the type name after the keyword operator (in this case, int). There is no return type specified before the keyword operator.

408

Part III Creating Components

When declaring your own conversion operators, you must specify whether they are implicit conversion operators or explicit conversion operators. You do this by using the implicit and explicit keywords. For example, the Hour to int conversion operator mentioned earlier is im-

plicit, meaning that the C# compiler can use it implicitly (without requiring a cast):

class Example

{

public static void MyOtherMethod(int parameter) { ... } public static void Main()

{

Hour lunch = new Hour(12);

Example.MyOtherMethod(lunch); // implicit Hour to int conversion

}

}

If the conversion operator had been declared explicit, the preceding example would not have compiled, because an explicit conversion operator requires an explicit cast:

Example.MyOtherMethod((int)lunch); // explicit Hour to int conversion

When should you declare a conversion operator as explicit or implicit? If a conversion is

always safe, does not run the risk of losing information, and cannot throw an exception, it can be defined as an implicit conversion. Otherwise, it should be declared as an explicit conver-

sion. Converting from an Hour to an int is always safe—every Hour has a corresponding int value—so it makes sense for it to be implicit. An operator that converts a string to an Hour should be explicit, as not all strings represent valid Hours. (The string “7” is fine, but how would you convert the string “Hello, World” to an Hour?)

Creating Symmetric Operators, Revisited

Conversion operators provide you with an alternative way to resolve the problem of providing symmetric operators. For example, instead of providing three versions of operator+ (Hour + Hour, Hour + int, and int + Hour) for the Hour structure, as shown earlier, you can provide a single version of operator+ (that takes two Hour parameters) and an implicit int to

Hour conversion, like this:

struct Hour

{

public Hour(int initialValue)

{

this.value = initialValue;

}

public static Hour operator+(Hour lhs, Hour rhs)

{

return new Hour(lhs.value + rhs.value);

}

Chapter 21 Operator Overloading

409

public static implicit operator Hour (int from)

{

return new Hour (from);

}

...

private int value;

}

If you add an Hour to an int (in either order), the C# compiler automatically converts the int to an Hour and then calls operator+ with two Hour arguments:

void Example(Hour a, int b)

{

Hour eg1 = a + b; // b converted to an Hour Hour eg2 = b + a; // b converted to an Hour

}

Adding an Implicit Conversion Operator

In the following exercise, you will modify the digital clock application from the preceding exercise. You will add an implicit conversion operator to the Second structure and remove the

operators that it replaces.

Write the conversion operator

1.Return to Visual Studio 2008, displaying the Operators project. Display the Clock.cs file in the Code and Text Editor window, and examine the tock method again:

private void tock()

{

this.second++;

if (this.second == 0)

{

this.minute++;

if (this.minute == 0)

{

this.hour++;

}

}

}

Notice the statement if (this.second == 0) shown in bold in the preceding code example. This fragment of code compares a Second to an int using the == operator.

2.Display the Second.cs file in the Code and Text Editor window.

The Second structure currently contains three overloaded implementations of operator== and three overloaded implementations of operator!=. Each operator is overloaded for the parameter type pairs (Second, Second), (Second, int), and (int, Second).

410Part III Creating Components

3.In the Second structure, comment out the four versions of operator== and operator!= that take one Second and one int parameter. (Do not comment out the operators that take two Second parameters.) The following two operators should be the only versions of operator== and operator!= remaining in the Second structure:

struct Second

{

...

public static bool operator==(Second lhs, Second rhs)

{

return lhs.value == rhs.value;

}

public static bool operator!=(Second lhs, Second rhs)

{

return lhs.value != rhs.value;

}

...

}

4.On the Build menu, click Build Solution.

The build fails with the following error message:

Operator ‘==’ cannot be applied to the operands of type ‘Operators.Second’ and ‘int’

Removing the operators that compare a Second and an int cause the statement if (this. second == 0) highlighted in step 1 to fail to compile.

5.In the Code and Text Editor window, add an implicit conversion operator to the Second structure that converts from an int to a Second.

The conversion operator should appear as shown in bold here:

struct Second

{

...

public static implicit operator Second (int arg)

{

return new Second(arg);

}

...

}

6.On the Build menu, click Build Solution.

The program successfully builds this time because the conversion operator and the

remaining two operators together provide the same functionality as the four deleted operator overloads. The only difference is that using an implicit conversion operator is potentially a little slower than not using an implicit conversion operator.

7.On the Debug menu, click Start Without Debugging. Verify that the application still works correctly.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]