10.9.3 转换运算符
转换运算符声明引入用户定义的转换(第 6.4 节),此转换可以扩充预定义的隐式和显式转换。
包含 implicit
关键字的转换运算符声明引入用户定义的隐式转换。隐式转换可以在多种情况下发生,包括函数成员调用、强制转换表达式和赋值。第
6.1 节对此有进一步描述。
包含 explicit
关键字的转换运算符声明引入用户定义的显式转换。显式转换可以发生在强制转换表达式中,第
6.2 节中对此进行了进一步描述。
一个转换运算符从某个源类型(即该转换运算符的参数的类型)转换到一个目标类型(即该转换运算符的返回类型)。如果下列条件都为真,则允许类或结构声明一个从源类型 S
到目标类型 T
的转换:
S
和T
是不同的类型。S
和T
中总有一个是声明了该运算符的类类型或结构类型。S
和T
都不是object
或接口类型。T
不是S
的基类,S
也不是T
的基类。
从第二条规则可以推知,转换运算符必须将声明了该运算符的类或结构类型或者作为目标类型,或者作为源类型。例如,一个类或结构类型 C
可以定义从 C
到 int
和从 int
到 C
的转换,但是不能定义从 int
到 bool
的转换。
不能重新定义一个已存在的预定义转换。因此,不允许转换运算符将 object
转换为其他类型或将其他类型转换为 object
,这是因为已存在一些隐式和显式转换来执行 object
和所有其他类型之间的转换。同样,转换的源类型和目标类型不能是对方的基类型,这是由于已经存在这样的转换。
用户定义的转换不能用于在接口类型和其他类型之间进行转换。具体说来,这条规定确保了在转换为接口类型时不会发生任何用户定义的转换,以及只有在被转换的对象实际上实现了指定的接口类型时,到该接口类型的转换才会成功。
转换运算符的签名由源类型和目标类型组成。(请注意,这是唯一一种其返回类型参与签名的成员形式。)转换运算符的 implicit
或 explicit
类别不是运算符签名的组成部分。因此,类或结构不能同时声明具有相同源类型和目标类型的 implicit
和 explicit
转换运算符。
一般来说,如果设计一个用户定义的隐式转换,就应当确保执行该转换时决不会引发异常,并且也决不会丢失信息。如果用户定义的转换可能导致引发异常(例如,由于源参数超出范围)或丢失信息(如放弃高序位),则该转换应该定义为显式转换。
在下面的示例中
using System;
public 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) {
return d.value;
}
public static explicit operator Digit(byte b) {
return new Digit(b);
}
}
从
Digit
到 byte
的转换是隐式的,这是因为它永远不会引发异常或丢失信息,但是从 byte
到 Digit
的转换是显式的,这是由于 Digit
只能表示 byte
的可能值的一个子集。class Digit { public Digit(double d) { val = d; } public double val; // ...other members // User-defined conversion from Digit to double public static implicit operator double(Digit d) { return d.val; } // User-defined conversion from double to Digit public static implicit operator Digit(double d) { return new Digit(d); } } class Program { static void Main(string[] args) { Digit dig = new Digit(7); //This call invokes the implicit "double" operator double num = dig; //This call invokes the implicit "Digit" operator Digit dig2 = 12; Console.WriteLine("num = {0} dig2 = {1}", num, dig2.val); Console.ReadLine(); } }