is 操作符只判断类型,不转换
is用于运行时检查对象是否属于某个类型(或其派生类型),返回
true或
false,**不会执行任何类型转换**。它不抛异常,也不改变原变量值。
常见错误是以为
is后能直接用转型结果:
if (obj is string) {
Console.WriteLine(obj.Length); // 编译错误!obj 还是 object 类型
}正确写法必须显式转换(或用模式匹配):
老写法:if (obj is string) { string s = (string)obj; ... }
C# 7+ 推荐:if (obj is string s) { Console.WriteLine(s.Length); } —— 这里 s是新声明的局部变量,不是
obj被“改类型”了
as 操作符尝试转换,失败返回 null
as尝试将对象引用转换为指定引用类型或可空类型。**仅适用于引用类型和可空值类型**;对非可空值类型(如
int、
DateTime)使用会编译报错。
它不抛异常,转换失败时返回
null(对可空值类型返回
null值)。但要注意:如果目标类型是
string、
object等引用类型,源为
null时也返回
null,无法区分“本来就是 null”和“转换失败”。 合法:
var s = obj as string;(
obj是
object) 非法:
var i = obj as int;→ 编译错误,
int是非可空值类型 合法(可空):
var i = obj as int?;
is 和 as 组合使用容易引发两次类型检查
这是性能和可读性上最常被忽略的问题。下面这段代码效率低且冗余:
if (obj is string) {
string s = obj as string; // 又做了一次运行时类型检查
Process(s);
}CLR 在
is和
as中都会执行相同的类型判定逻辑,相当于重复工作。C# 7+ 的模式匹配语法正是为解决这个而生: ✅ 推荐:
if (obj is string s) Process(s);—— 一次检查,一次赋值 ✅ 或者直接用
as+ 空检查:
string s = obj as string; if (s != null) Process(s);❌ 避免
is后紧跟
as,除非你明确需要中间的布尔判断逻辑
值类型场景下 as 完全不可用,is 仍可用
对值类型(如
int、
Guid、自定义
struct),
as不能用,编译器直接拒绝。但
is可以配合装箱/拆箱判断:
object obj = 42;
if (obj is int) { /* true */ }
// var x = obj as int; // 编译错误若要安全获取值类型,只能用
is+ 显式拆箱,或用泛型方法(如
Convert.ChangeType);注意拆箱必须类型完全一致,
obj is int成立不代表
(int)obj一定成功(比如
obj是
long装箱的 42,
is int为
false,但有人误以为可以强转)。
真正容易被忽略的是:
is对值类型的判断本质是“是否为该类型装箱后的对象”,它不支持隐式转换检查(比如
long到
int),这点和
as的适用范围限制一起,让值类型的安全转换始终比引用类型更繁琐。
