c# is 和 as 操作符的区别

is只判断类型不转换,as尝试转换失败返回null;二者组合使用会导致重复类型检查,C# 7+推荐用模式匹配if(obj is string s)一次性完成判断与赋值。

is 操作符只判断类型,不转换

is 用于运行时检查对象是否属于某个类型(或其派生类型),返回 truefalse,**不会执行任何类型转换**。它不抛异常,也不改变原变量值。

常见错误是以为 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 尝试将对象引用转换为指定引用类型或可空类型。**仅适用于引用类型和可空值类型**;对非可空值类型(如 intDateTime)使用会编译报错。

它不抛异常,转换失败时返回 null(对可空值类型返回 null 值)。但要注意:如果目标类型是 stringobject 等引用类型,源为 null 时也返回 null,无法区分“本来就是 null”和“转换失败”。

  • 合法:var s = obj as string;objobject
  • 非法: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 在 isas 中都会执行相同的类型判定逻辑,相当于重复工作。C# 7+ 的模式匹配语法正是为解决这个而生:

  • ✅ 推荐:if (obj is string s) Process(s); —— 一次检查,一次赋值
  • ✅ 或者直接用 as + 空检查:string s = obj as string; if (s != null) Process(s);
  • ❌ 避免 is 后紧跟 as,除非你明确需要中间的布尔判断逻辑

值类型场景下 as 完全不可用,is 仍可用

对值类型(如 intGuid、自定义 struct),as 不能用,编译器直接拒绝。但 is 可以配合装箱/拆箱判断:

object obj = 42;
if (obj is int) { /* true */ }
// var x = obj as int; // 编译错误

若要安全获取值类型,只能用 is + 显式拆箱,或用泛型方法(如 Convert.ChangeType);注意拆箱必须类型完全一致,obj is int 成立不代表 (int)obj 一定成功(比如 objlong 装箱的 42,is intfalse,但有人误以为可以强转)。

真正容易被忽略的是:is 对值类型的判断本质是“是否为该类型装箱后的对象”,它不支持隐式转换检查(比如 longint),这点和 as 的适用范围限制一起,让值类型的安全转换始终比引用类型更繁琐。