C# 中的 is 运算符主要用于类型检查 和模式匹配 。它用于在运行时检查对象的类型,返回一个布尔值,指示表达式的结果是否与指定的类型兼容,或者是否可以转换为该类型。is 运算符在类型转换前进行检查,可以有效地避免因类型不匹配而导致的 InvalidCastException 异常 。
一、is 运算符的基本用法
is 运算符的基本语法为:expression is type。如果 expression 的结果非空,并且可以通过引用转换、装箱转换、拆箱转换或用户定义的隐式/显式转换转换为 type 类型,则结果为 true;否则为 false 。
| 场景 | 描述 | 返回值 |
|---|---|---|
| 引用转换 | 检查对象是否为特定类或其派生类的实例。 | true / false |
| 装箱/拆箱转换 | 检查值类型与 object 或接口的兼容性。 |
true / false |
| 类型匹配 | 在模式匹配中,检查并声明新变量。 | true(并赋值) / false |
以下是一个基本示例:
csharp
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 运算符的功能得到了极大增强,支持声明模式 和类型模式,可以在检查类型的同时将结果赋值给一个新变量 。
csharp
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,以及它们各自适用的场景:
csharp
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字面量一起使用,简洁地检查可空值类型是否具有值。csharpint? 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。"); } -
常量模式 :
检查表达式结果是否等于指定的常量值。
csharpobject input = 100; if (input is 100) { Console.WriteLine("输入等于 100。"); } -
与
switch语句/表达式结合 :在
switch中,is模式匹配可以非常强大地处理多种类型。csharpstatic 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# 中进行安全类型检查和模式匹配的核心工具。从基本的类型兼容性判断到现代的模式匹配语法,它帮助开发者编写出更安全、更清晰、更健壮的代码。