C#中is运算符的正确用法
C# 中的is运算符主要用于类型检查和模式匹配。它用于在运行时检查对象的类型,返回一个布尔值,指示表达式的结果是否与指定的类型兼容,或者是否可以转换为该类型。is运算符在类型转换前进行检查,可以有效地避免因类型不匹配而导致的InvalidCastException异常 。
一、is运算符的基本用法
is运算符的基本语法为:expression is type。如果expression的结果非空,并且可以通过引用转换、装箱转换、拆箱转换或用户定义的隐式/显式转换转换为type类型,则结果为true;否则为false。
| 场景 | 描述 | 返回值 |
|---|---|---|
| 引用转换 | 检查对象是否为特定类或其派生类的实例。 | true/false |
| 装箱/拆箱转换 | 检查值类型与object或接口的兼容性。 | true/false |
| 类型匹配 | 在模式匹配中,检查并声明新变量。 | true(并赋值) /false |
以下是一个基本示例:
using System; class Animal { } class Dog : Animal { } class Cat : Animal { } class Program { static void Main() { Animal myAnimal = new Dog(); // 使用 is 进行类型检查 if (myAnimal is Dog) { Console.WriteLine("myAnimal 是一只 Dog。"); } if (myAnimal is Cat) { Console.WriteLine("myAnimal 是一只 Cat。"); // 这行不会执行 } else { Console.WriteLine("myAnimal 不是一只 Cat。"); } // 检查基类 if (myAnimal is Animal) { Console.WriteLine("myAnimal 是一个 Animal。"); // 始终为 true } // 检查 null Animal nullAnimal = null; Console.WriteLine(nullAnimal is Animal); // 输出:False,因为表达式结果为 null } }运行上述代码,输出结果为:
myAnimal 是一只 Dog。 myAnimal 不是一只 Cat。 myAnimal 是一个 Animal。 False二、is运算符与模式匹配(C# 7.0 及更高版本)
从 C# 7.0 开始,is运算符的功能得到了极大增强,支持声明模式和类型模式,可以在检查类型的同时将结果赋值给一个新变量 。
using System; class Animal { public string Name { get; set; } } class Dog : Animal { public void Bark() => Console.WriteLine("Woof!"); } class Cat : Animal { public void Meow() => Console.WriteLine("Meow!"); } class Program { static void MakeSound(Animal animal) { // 使用 is 进行类型检查和变量声明 if (animal is Dog dog) // 如果 animal 是 Dog 类型,则将其赋值给新变量 dog { Console.WriteLine($"找到一只狗,名字是 {dog.Name}。"); dog.Bark(); } else if (animal is Cat cat) // 如果 animal 是 Cat 类型,则将其赋值给新变量 cat { Console.WriteLine($"找到一只猫,名字是 {cat.Name}。"); cat.Meow(); } else { Console.WriteLine("未知动物类型。"); } } static void Main() { Animal myDog = new Dog { Name = "Buddy" }; Animal myCat = new Cat { Name = "Whiskers" }; MakeSound(myDog); MakeSound(myCat); } }输出:
找到一只狗,名字是 Buddy。 Woof! 找到一只猫,名字是 Whiskers。 Meow!关键点:模式匹配中的is表达式animal is Dog dog不仅检查类型,还在条件为真时,将animal安全地转换为Dog类型并赋值给新变量dog。这个新变量的作用域限定在紧随其后的if语句块内。
三、is与as运算符的对比与选择
is和as都用于安全的类型转换,但目的和行为不同。as运算符尝试进行转换,如果失败则返回null,而is仅进行检查 。
| 特性 | is运算符 | as运算符 |
|---|---|---|
| 主要目的 | 类型检查。判断对象是否与给定类型兼容。 | 安全类型转换。尝试将对象转换为指定类型。 |
| 返回值 | bool(true或false)。 | 目标类型的值(转换成功)或null(转换失败)。 |
| 异常 | 从不抛出异常。 | 从不抛出异常。转换失败时返回null。 |
| 典型用法 | 在转换前进行条件判断。 | 直接尝试转换,并处理可能的null结果。 |
| 模式匹配 | 支持(C# 7.0+),可同时声明变量。 | 不支持。 |
以下示例展示了如何结合使用is和as,以及它们各自适用的场景:
using System; class Animal { } class Dog : Animal { public void Fetch() => Console.WriteLine("Fetching ball..."); } class Program { static void ProcessWithIs(Animal animal) { // 使用 is 进行检查,然后进行强制转换 if (animal is Dog) { Dog dog = (Dog)animal; // 因为已经用 is 检查过,所以这里是安全的 dog.Fetch(); } else { Console.WriteLine("不是狗,无法执行 Fetch。"); } } static void ProcessWithAs(Animal animal) { // 使用 as 进行转换,然后检查结果是否为 null Dog dog = animal as Dog; if (dog != null) { dog.Fetch(); } else { Console.WriteLine("不是狗,无法执行 Fetch。"); } } static void ProcessWithPatternMatching(Animal animal) { // 使用 is 模式匹配(最简洁、高效的方式) if (animal is Dog safeDog) // 检查并转换一步完成 { safeDog.Fetch(); } else { Console.WriteLine("不是狗,无法执行 Fetch。"); } } static void Main() { Animal myDog = new Dog(); Animal notADog = new Animal(); Console.WriteLine("=== 使用 is + 强制转换 ==="); ProcessWithIs(myDog); ProcessWithIs(notADog); Console.WriteLine(" === 使用 as ==="); ProcessWithAs(myDog); ProcessWithAs(notADog); Console.WriteLine(" === 使用 is 模式匹配 ==="); ProcessWithPatternMatching(myDog); ProcessWithPatternMatching(notADog); } }输出:
=== 使用 is + 强制转换 === Fetching ball... 不是狗,无法执行 Fetch。 === 使用 as === Fetching ball... 不是狗,无法执行 Fetch。 === 使用 is 模式匹配 === Fetching ball... 不是狗,无法执行 Fetch。选择建议:
- 如果只需要检查类型,不进行后续操作:使用
is(例如if (obj is MyType))。 - 如果检查后需要转换为该类型并使用:优先使用 C# 7.0 的
is模式匹配(if (obj is MyType var)),因为它最简洁且性能通常更优(避免了双重类型检查)。 - 如果转换失败时你希望得到一个
null值进行其他处理:使用as运算符(例如var result = obj as MyType)。 - 避免使用
is检查后再用as转换:这是一种低效的做法,因为进行了两次类型检查。应直接使用模式匹配或as。
四、is运算符的其他应用场景
检查可空值类型:
is运算符可以与null字面量一起使用,简洁地检查可空值类型是否具有值。int? nullableInt = 42; if (nullableInt is int value) // 检查 nullableInt 是否有值,并将其赋值给 value { Console.WriteLine($"有值: {value}"); } else { Console.WriteLine("无值 (null)"); } nullableInt = null; if (nullableInt is null) // 专门检查是否为 null { Console.WriteLine("它是 null。"); }常量模式:
检查表达式结果是否等于指定的常量值。object input = 100; if (input is 100) { Console.WriteLine("输入等于 100。"); }与
switch语句/表达式结合:
在switch中,is模式匹配可以非常强大地处理多种类型。static string DescribeShape(object shape) { return shape switch { Circle c => $"圆形,半径 {c.Radius}", Rectangle r => $"矩形,长 {r.Length} 宽 {r.Width}", Triangle t => $"三角形", null => "未提供形状", _ => "未知形状" // 默认情况 }; }
五、性能与最佳实践
- 性能:
is运算符的类型检查是高效的运行时操作。模式匹配版本的is在性能上通常优于先is后强制转换的方式,因为它避免了重复的类型判断 。 - 可读性:使用
is模式匹配 (if (obj is MyType var)) 可以极大地提高代码的可读性和简洁性,是推荐的现代 C# 写法。 - 避免异常:始终使用
is(或as)来替代直接的强制转换(Type)obj,除非你百分之百确定对象的类型。直接的强制转换在失败时会抛出InvalidCastException,而is和as提供了安全的替代方案 。
总之,is运算符是 C# 中进行安全类型检查和模式匹配的核心工具。从基本的类型兼容性判断到现代的模式匹配语法,它帮助开发者编写出更安全、更清晰、更健壮的代码。
参考来源
- C#中的is和as运算符
- C#中as和is关键字作用
- 使用 as 和 is 运算符安全地进行强制转换
- 【C#】尝试类型转换异常System.InvalidCastException: Specified cast is not valid.解决方案
- c# is / as 实例
- C#--运算符
