C#作为静态强类型语言,要想使用好它,掌握好几种类型判断的方法是很基础也很必要的。如我们所知,最常用的几种就是,is,as,铸型cast和自定义类型转换。但是这几种之间有什么区别是一个容易混淆的地方,现在让我们来看看。
is
is如下场合返回true:
- 实例属于该类型
class Person { }
Person p = new Person();
bool result = p is Person;
- 实例属于该类型的派生类型
class Manager : Person { }
Manager m = new Manager();
bool result = m is Person;
- 实例实现了该接口
interface IEmpty { }
class ImplememntEmpty : IEmpty { }
ImplememntEmpty instance = new ImplememntEmpty();
var result = instance is IEmpty;
- 实例拆箱之后类型可以匹配
var obj = 10l;
var result = obj is long;
注意,is关键字不会考虑用户自定的类型转换,无论是implicit还是explicit都不会考虑。
as
as关键字和is紧紧关联,在内部,as关键字是这么处理的,
instace is type ? (type)instance : (type)null
所以在使用as的时候,有两个地方需要注意。
- 要想通过as完成转型,首先要确保实例能通过is测试,也就是说,用户自定义的类型转换还是不会被考虑。
- as转型失败的时候会返回一个null,所以as的目标必须是能引用类型或者是可空值类型,否则编译器会报错。考虑下面这个例子,
object obj = 10l;
var result = obj as long; //这里会报错
正确的写法是
object obj = 10l;
var result = obj as long?; //这里就没问题了,这样确保了在转型失败的情况下,null可以赋值给long?
cast
cast分为显式转型和隐式转型,当编译器检测出转型不会造成数据精度损失,或者不会有转型风险的时候,不需要使用显式转型,反之,如果显式转型是必须的。
- 对于值类型来说,从低精度到高精度的转型被视作无精度损失,是安全的,比如从int到long, 从float到double。反之则需要显式转型,旨在告诉编译器用户知晓这种转型可能会影响精度,用户甘愿接受这种精度损失。
float f = 10;
double d = f;
int i = 10;
long l = i;
//下面开始是显式转型
i = (int)l;
f = (float)d;
- 对于引用类型来说,从子类像父类的转换总是安全的,成功的,这里不需要显式转型。但是反过来,显式转型是需要的,因为从父类转换到子类不总是会成功。
class Person { }
class Manager : Person { }
Manager m = new Manager();
Person p = m;
//以下需要显式转型,请注意,在这里如果p不是一个Manager,显式转型会抛异常。所以在使用显式转型的地方,建议使用as或者加入try catch以增加代码安全
m = (Manager)p;
用户自定义类型转换
上面的Cast可以使用到用户自定义类型转换,和Cast有显式和隐式一样,用户自定义类型转换也有显式隐式之分。分别用关键字explicit和implicit区分,在调用的时候,显式转换可以使用用户自定义的显式和隐式转换,而隐式转换就要求用户必须提供自定义的隐式转换。
用户自定义类型转换的作用在于给原本可能风马牛不相及的类型之间提供了一个转换通道,让它们相互转换成为了可能,而这一切,在自定义类型转换出来之前,很可能是编译器禁止的。
class Company { }
class Corporation { }
Company c = new Company();
Corporation cor = (Corporation)c; //编译器会报错,声称无法将Company类型转换成Corporation
添加显式自定义类型转换
class Company
{
public static explicit operator Corporation(Company c)
{
return new Corporation();
}
}
这样编译器就能知道我们提供了一个自定义转换从Company到Corporation,这样上面那段代码就可以工作了。
隐式用户自定义转换类似,只是把关键字explicit换成implicit即可。