C#中一个继承自C++的优良特性是可以自定义类型转换。
以前在C++中这样写:
class CBrush{
//摘自MFC中CBrush类的部分代码。
public:
operator HBRUSH() const;
};
在C#中也有类似的写法,不过要注意的是C#中这种类型转换函数必须是静态的,相比较与C++,C++中的类型转换则就没有这样的限制。
此外,自定义类型转换可以设置修饰符explicit或implicit。explicit表示显式,也就是在使用这个转换时必须显式写在代码中。implicit修饰符表示隐式,隐式使用即使用时不必要显式写出来,如下例:
class A{
public static explicit operator A(B b){省略内容} //这是一个显式转换
public static implicit operator B(A a){省略内容} //这是一个隐式转换
}
B b=new B();
A a=(A)b; //显式使用显式转换
b=a; //隐式使用隐式转换
自定义转换还需要注意的一点与as这个运算符有关。as一般情况下相当于 expression is type ? (type)expression : (type)null但只计算一次 expression。as 运算符只执行引用转换和装箱转换。as 运算符无法执行其他转换,如用户定义的转换,这类转换应使用强制转换表达式来执行。
然后就是发现在值类型和引用类型之间的自定义转换比较麻烦,装箱和不装箱的值类型在转换时的行为是有区别的。
下面的MyInt是一个引用类型,用作测试和值类型int之间的转换。
自定义转换测试
class MyInt
{
int data;
public MyInt():this(10){}
public MyInt(int val)
{
data = val;
}
public static explicit operator int(MyInt x)
{
return x.data;
}
public static explicit operator MyInt(int x)
{
MyInt a = new MyInt();
a.data = x;
return a;
}
public override string ToString()
{
return data.ToString();
}
}
static void Main(string[] args)
{
try
{
MyInt myInt = new MyInt();
int t = 100;
myInt = (MyInt)t; //执行自定义转换 operator MyInt(int)
Console.WriteLine("myInt={0}", (myInt == null ? "null" : myInt.ToString())); //myInt=100
int x = (int)myInt; //执行自定义转换 operator int(MyInt)
Console.WriteLine("x={0}", x); //x=100
// myInt = x as MyInt; //错误CS0039: 无法通过引用转换、装箱转换、取消装箱转换、包装转换或 Null 类型转换将类型“int”转换为“MyInt”
object xobj = x; //把x装箱
myInt = xobj as MyInt; //as不会调用自定义转换
Console.WriteLine("myInt={0}", (myInt == null ? "null" : myInt.ToString())); //myInt=null
myInt = (MyInt)xobj; //装箱后这样转换会出异常
//必须要显式拆箱再转换,如:myInt=(MyInt)(int)xobj;
Console.WriteLine("myInt={0}", (myInt == null ? "null" : myInt.ToString())); //这一句不会执行
}
catch(Exception e)
{
Console.Error.WriteLine(e); //System.InvalidCastException ...
}
}