下面我主要介绍用于类型转换的关键字:explicit、implicit 和 operator。
2.1 explicit关键字
explicit 关键字用于声明必须使用强制转换来调用的用户定义的类型转换运算符。
static explicit operator target_type { source_type identifier }
参数:
- target_type 目标类型
- source_type 源类型。
- identifier Something。
注意:
- 转换运算符将源类型转换为目标类型。与隐式转换不同,必须通过强制转换的方式来调用显式转换运算符。如果转换操作可能导致异常或丢失信息,则应将其标记为 explicit。这可以防止编译器无提示地调用可能产生无法预见后果的转换操作。
实例:下例定义了一个Digit结构,该结构表示单个十进制数字。还定义了一个运算符,用于将byte类转换为Digit结构,但因为并非所有字节都可以转换为Digit,所以该转换时显示的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace explicits
{
struct Digit
{
byte value;
public Digit(byte value)
{
if (value < 0 || value > 9) throw new ArgumentException();
this.value = value;
}
//定义byte到Digit的显示转换运算符
public static explicit operator Digit(byte b)
{
Console.WriteLine("conversion occured");
return new Digit(b);
}
}
class Program
{
static void Main(string[] args)
{
byte b = 3;
Digit d = (Digit)b; //显示转换
Console.Read();
}
}
}
2.1 implicit 关键字
implicit 关键字用于声明隐式的用户定义类型转换运算符。
static implicit operator target_type { source_type identifier }
注意:
- 隐式转换可以通过消除不必要的类型转换来提高源代码的可读性。但是,因为可以在程序员未指定的情况下发生隐式转换,因此必须注意防止令人不愉快的后果。一般情况下,隐式转换运算符应当从不引发异常并且从不丢失信息,以便可以在程序员不知晓的情况下安全使用它们。如果转换运算符不能满足那些条件,则应将其标记为 explicit。
2.3 operator关键字
operator 关键字用于在类或结构声明中声明运算符。运算符声明可以采用下列四种形式之一:
-
public static result-type operator unary-operator ( op-type operand )
-
public static result-type operator binary-operator ( op-type operand, op-type2 operand2 )
-
public static implicit operator conv-type-out ( conv-type-in operand )
-
public static explicit operator conv-type-out ( conv-type-in operand )
参数:
- result-type 运算符的结果类型。
- unary-operator 下列运算符之一:+ - ! ~ ++ — true false
- op-type 第一个(或唯一一个)参数的类型。
- operand 第一个(或唯一一个)参数的名称。
- binary-operator 其中一个:+ - * / % & | ^ << >> == != > < >= <=
- op-type2 第二个参数的类型。
- operand2 第二个参数的名称。
- conv-type-out 类型转换运算符的目标类型。
- conv-type-in 类型转换运算符的输入类型。
注意:
- 前两种形式声明了用户定义的重载内置运算符的运算符。并非所有内置运算符都可以被重载(请参见可重载的运算符)。op-type 和 op-type2 中至少有一个必须是封闭类型(即运算符所属的类型,或理解为自定义的类型)。例如,这将防止重定义整数加法运算符。
- 后两种形式声明了转换运算符。conv-type-in 和 conv-type-out 中正好有一个必须是封闭类型(即,转换运算符只能从它的封闭类型转换为其他某个类型,或从其他某个类型转换为它的封闭类型)。
- 运算符只能采用值参数,不能采用 ref 或 out 参数。
- C# 要求成对重载比较运算符。如果重载了==,则也必须重载!=,否则产生编译错误。同时,比较运算符必须返回bool类型的值,这是与其他算术运算符的根本区别。
- C# 不允许重载=运算符,但如果重载例如+运算符,编译器会自动使用+运算符的重载来执行+=运算符的操作。
- 运算符重载的其实就是函数重载。首先通过指定的运算表达式调用对应的运算符函数,然后再将运算对象转化为运算符函数的实参,接着根据实参的类型来确定需要调用的函数的重载,这个过程是由编译器完成。
- 任何运算符声明的前面都可以有一个可选的属性(C# 编程指南)列表。
下面是一个类型转换关键字:explicit、implicit 和 operator 的一个例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Conversions
{
public class Fraction
{
private int numerator;
private int denominator;
public Fraction(int numerator, int denominator)
{
Console.WriteLine("In Fraction Constructor(int,int)");
this.numerator = numerator;
this.denominator = denominator;
}
public static implicit operator Fraction(int theInt)
{
Console.WriteLine("In implicit conversion to Fraction");
return new Fraction(theInt);
}
public static explicit operator int(Fraction theFraction)
{
Console.WriteLine("In explicit conversion to int");
return theFraction.numerator / theFraction.denominator;
}
public static bool operator ==(Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator ==");
if (lhs.denominator == rhs.denominator && lhs.numerator == rhs.numerator)
{
return true;
}
//此处省去处理不同分母分数的代码
return false;
}
public static bool operator !=(Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator !=");
return !(lhs == rhs);
}
public override bool Equals(object o)
{
Console.WriteLine("In method Equals");
if (!(o is Fraction))
{
return false;
}
return this == (Fraction)o;
}
public static Fraction operator +(Fraction lhs, Fraction rhs)
{
Console.WriteLine("In operator+");
if (lhs.denominator == rhs.denominator)
{
return new Fraction(lhs.numerator + rhs.numerator, lhs.denominator);
}
//对不同的分母分数的简单解决方案
//1/2 + 3/4==(1*4) + (3*2) /(2*4) ==10/8
int firstProduct = lhs.numerator * rhs.denominator;
int secondProduct = rhs.numerator * lhs.denominator;
return new Fraction(
firstProduct + secondProduct,
lhs.denominator * rhs.denominator);
}
public override string ToString()
{
String s = numerator.ToString() + "/" + denominator.ToString();
return s;
}
}
class Program
{
static void Main(string[] args)
{
Fraction f1 = new Fraction(3, 4);
Console.WriteLine("f1: {0}", f1.ToString());
Fraction f2 = new Fraction(2, 4);
Console.WriteLine("f2: {0}", f2.ToString());
Fraction f3 =f1 + f2;
Console.WriteLine("f1 + f2 = f3: {0}", f3.ToString());
Fraction f4 = f3 + 5;
Console.WriteLine("f3 +5= f4: {0}", f4.ToString());
Fraction f5 = new Fraction(2, 4);
if (f5 == f2)
{
Console.WriteLine("F5: {0} ==F2: {1}", f5.ToString(), f2.ToString());
}
Console.Read();
}
}
}