C#中is运算符的正确用法

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 语句块内。

三、isas 运算符的对比与选择

isas 都用于安全的类型转换,但目的和行为不同。as 运算符尝试进行转换,如果失败则返回 null,而 is 仅进行检查 。

特性 is 运算符 as 运算符
主要目的 类型检查。判断对象是否与给定类型兼容。 安全类型转换。尝试将对象转换为指定类型。
返回值 booltruefalse)。 目标类型的值(转换成功)或 null(转换失败)。
异常 从不抛出异常 从不抛出异常 。转换失败时返回 null
典型用法 在转换前进行条件判断。 直接尝试转换,并处理可能的 null 结果。
模式匹配 支持(C# 7.0+),可同时声明变量。 不支持。

以下示例展示了如何结合使用 isas,以及它们各自适用的场景:

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。

选择建议

  1. 如果只需要检查类型,不进行后续操作 :使用 is(例如 if (obj is MyType))。
  2. 如果检查后需要转换为该类型并使用优先使用 C# 7.0 的 is 模式匹配if (obj is MyType var)),因为它最简洁且性能通常更优(避免了双重类型检查)。
  3. 如果转换失败时你希望得到一个 null 值进行其他处理 :使用 as 运算符(例如 var result = obj as MyType)。
  4. 避免使用 is 检查后再用 as 转换 :这是一种低效的做法,因为进行了两次类型检查。应直接使用模式匹配或 as

四、is 运算符的其他应用场景

  1. 检查可空值类型
    is 运算符可以与 null 字面量一起使用,简洁地检查可空值类型是否具有值。

    csharp 复制代码
    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。");
    }
  2. 常量模式

    检查表达式结果是否等于指定的常量值。

    csharp 复制代码
    object input = 100;
    if (input is 100)
    {
        Console.WriteLine("输入等于 100。");
    }
  3. switch 语句/表达式结合

    switch 中,is 模式匹配可以非常强大地处理多种类型。

    csharp 复制代码
    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,而 isas 提供了安全的替代方案 。

总之,is 运算符是 C# 中进行安全类型检查和模式匹配的核心工具。从基本的类型兼容性判断到现代的模式匹配语法,它帮助开发者编写出更安全、更清晰、更健壮的代码。


参考来源

相关推荐
战族狼魂16 小时前
上位机软件开发完整学习路线与项目实战指南
单片机·c#·wpf
吴可可12316 小时前
Teigha自定义图元开发详解
c#
weixin_5206498716 小时前
C#队列Queue详解
开发语言·数据库·c#
吴可可12316 小时前
C#索引器使用详解
c#
雪豹阿伟17 小时前
10.C# —— 数组Array
c#·上位机
十贺17 小时前
【Unity开发字典】序列化基类
unity·c#·tcp
csdn_aspnet18 小时前
C# 算法 LeetCode 编号 70 - 爬楼梯
算法·leetcode·c#
魔法阵维护师18 小时前
从零开发游戏需要学习的c#模块,第二十五章(摄像机 —— 让世界比屏幕大)
学习·游戏·c#