C#中的is和as的用法及模式匹配

  is运算符

  is在C#7.0之前,主要用于检验实例是否是指定的类型,格式:  

    expr is type

  expr是一个具有值的表达式,type是一个类型。

  从C#7.0开始,is表达式将使用模式匹配来操作,格式:  

    expr is pattern

  expr是一个具有值的表达式,pattern是模式匹配,而C#7.0之前的用法则是模式匹配中的类型模式,更多模式匹配见:C#中的模式匹配

  我们常将is运算法用于类型检查上,本质上其实是采用对应的模式匹配而已,也就是说is的结果取决于expr值是否满足指定的模式,比如:  

  1、类型模式

复制代码
    Object value = "";
    if (value is string)
    {
        Console.WriteLine("value is String");
    }
    //输出:value is String
复制代码

  2、声明模式  

复制代码
    Object value = "String";
    if (value is string str)
    {
        Console.WriteLine($"value is {str}");
    }
    //输出:value is String
复制代码

  3、常量模式  

复制代码
    Object value = "String";
    if (value is "String")
    {
        Console.WriteLine($"value is String");
    }
    //输出:value is String
复制代码

  或者检查值是否是null:  

复制代码
    Object value = null;
    if (value is null)
    {
        Console.WriteLine($"value is null");
    }
    //输出:value is null
复制代码

  注:常量模式中的is表达式判断并不会调用用户重写的==运算符,因此 value==null 和 value is null 可能得到不一样的结果,详情见 C#中的模式匹配 中的的常量模式

  4、关系模式  

复制代码
    Object value = 1;
    if (value is < 4)
    {
        Console.WriteLine($"value less than 4");
    }
    //输出:value less than 4
复制代码

  5、逻辑模式  

复制代码
    Object value = 5;
    if (value is not string and > 1 and < 10)
    {
        Console.WriteLine($"value is not string and greater than 1 and less than 10");
    }
    //输出:value is not string and greater than 1 and less than 10
复制代码

  6、属性模式  

复制代码
    DateTime time = DateTime.Now;
    if (time is { Year: 2021, Month: var month, Day: var day, Hour: var hour, Minute: var minute, Second: var second })
    {
        Console.WriteLine($"time is {2021}-{month}-{day} {hour}:{minute}:{second}");
    }
    //输出:time is 2021-7-6 16:42:23
复制代码

  注:这个模式中,它会匹配Year是不是2021,如果是则将Month,Day等属性值赋值给对应的变量(这个是var模式),详情见 C#中的模式匹配 中的的属性模式和var模式

  7、位置模式

  这个模式跟解构表达式有关,所以在记录(record)中比较实用,例如:  

复制代码
    public record Point(int X, int Y);
    static void Main(string[] args)
    {
        Point p = new Point(100, 200);
        if (p is (100, 200))
        {
            Console.WriteLine($"value is (100, 200)");
        }
        //输出:value is (100, 200)
    }
复制代码

  8、var模式

  前面已经有看到var模式,它常和属性模式和位置模式结合,来接收一个中间变量 ,方便进行下一步的判断使用,比如:  

复制代码
    public record Point(int X, int Y);
    static void Main(string[] args)
    {
        Point p = new Point(100, 200);
        if (p is var (x, y))
        {
            Console.WriteLine($"x={x},y={y}");
        }
        //输出:x=100,y=200
    }
复制代码

  9、弃元模式

  is用弃元模式的场景不多,比如在解构时,只需要解构中的部分属性,那么其它属性就可以使用弃元丢弃了,例如:  

复制代码
    public record Point(int X, int Y);
    static void Main(string[] args)
    {
        Point p = new Point(100, 200);
        if (p is var (x, _))//y被丢弃了
        {
            Console.WriteLine($"x={x}");
        }
        //输出:x=100
    }
复制代码

 

  as运算符

  在C#7.0之前,is和as是一对,is用于判断运行时的实例是否属于指定的类型,而as则是将运行时的实例转换成指定的类型。

  在C#7.0之后,is功能得到很大的拓展,而as几乎没变。

  目前,as的作用用于类型的转换,转换的对象是:引用类型、可以为null的值类型(如 int?、long?、bool?等),如果转换成功,则返回的是转后的是对象,否则返回null,格式:  

    expr as type

  expr是一个有指的表达式,type是指定的转换类型。  

复制代码
    public void Convert(object value)
    {
        Program program = value as Program;//可以转换为引用类型,转换失败则返回null
        int? intValue = value as int?;//可以转换为null的值类型,转换失败则返回null
        bool b = value as bool;//不能转换,编译宝座,bool不是引用类型或者可以为null的值类型
    }
复制代码

  注意,as的转换很有限,只能是引用类型、可以为null的值类型,而且是一些简单的转换,比如装箱和取消装箱,用is来说,大致等价于:  

    expr is type varname ? varname : (type)null

  varname是一个变量名称,这里其实也就是一个三元运算符和is表达式的结合。

  

  总结

  is 和 as 是另个非常适用的语法糖,但是还有两点说明一下:

  1、is和as是安全的,它们不会抛出异常。

  2、is和as操作会不支持用户自定义的操作运算法和隐式转换。

  比如:  

复制代码
    public class ClassA
    {
        public static implicit operator ClassA(ClassB classB) => new ClassA();
        public static bool operator ==(ClassA classA1, ClassA classA2) => true;//与任何ClassA比较相等都是true
        public static bool operator !=(ClassA classA1, ClassA classA2) => false;//与任何ClassA比较不相等都是false
        public override bool Equals(object? obj) => true;//与任何值比较相等都是true
        public override int GetHashCode() => base.GetHashCode();
    }
    public class ClassB
    {
        public static bool operator ==(ClassB classA1, ClassB classA2) => false;//与任何ClassB比较相等都是false
        public static bool operator !=(ClassB classA1, ClassB classA2) => true;//与任何ClassB比较不相等都是true
        public override bool Equals(object? obj) => false;//与任何值比较相等都是false
        public override int GetHashCode() => base.GetHashCode();
    }
    static void Main(string[] args)
    {
        //值类型int可以隐式转换成long,==判断为true,但是is判断为false
        int i = 1;//int
        long l = i;//int可以隐式转换为long
        Console.WriteLine(l == i);//true
        Console.WriteLine(i is long);//false

        //引用类型ClassB可以隐式转换成ClassA,重写==运算导致所有返回值都是true,但是is运算返回false
        ClassB classB = new ClassB();
        ClassA classA = classB;//ClassB可以隐式转换为ClassA
        Console.WriteLine(classA == classB);//true
        Console.WriteLine(classB is ClassA);//false

        //引用类型ClassB为null,重写==运算导致所有返回值都是false,但是is运算返回true
        classB = null;//null值
        Console.WriteLine(classB == null);//false
        Console.WriteLine(classB is null);//true

        //引用类型ClassB可以隐式转换成ClassA,但是as运算不能转换,所以返回null
        object value = new ClassB();
        Console.WriteLine(object.Equals(null, value as ClassA));//true
    }
复制代码

 

  参考文档:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions

  

 

 

出处:https://www.cnblogs.com/shanfeng1000/p/14975706.html

posted on 2022-12-01 15:36  jack_Meng  阅读(2623)  评论(0编辑  收藏  举报

导航