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