c# is 和 as 浅看重制版

前言

当年写的比较差:https://www.cnblogs.com/aoximin/p/12965408.html,所以特来重新写一遍。

正文

首先为什么会出现is 和 as 呢?

因为是为了有需要检验的地方,如果直接使用显示转换的话,那么可能直接报错了。

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            object obj = new Student();
            var c = (Teacher)obj;
        }

        public class Student
        {
            
        }

        public class Teacher
        { 
        }
    }
}

比如这样肯定会报错的, 因为在运行的时候我们来查看一下il语句。

.method private hidebysig static 
	void Main (
		string[] args
	) cil managed 
{
	.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
		01 00 01 00 00
	)
	// Method begins at RVA 0x20a4
	// Header size: 12
	// Code size: 15 (0xf)
	.maxstack 1
	.entrypoint
	.locals init (
		[0] object obj,
		[1] class ConsoleApp4.Program/Teacher c
	)

	IL_0000: nop
	IL_0001: newobj instance void ConsoleApp4.Program/Student::.ctor()
	IL_0006: stloc.0
	IL_0007: ldloc.0
	IL_0008: castclass ConsoleApp4.Program/Teacher
	IL_000d: stloc.1
	IL_000e: ret
} // end of method Program::Main

会调用castclass 进行转换。

因为castclass 无法找到他们两个的转换方法(编写显示转换或者隐式转换的方法),他们也不是继承关系,所以会抛出异常。

但是也不要以为所以的强制转换就一定要编写啥编写显示转换或者隐式转换的方法或者是啥继承关系,还有一种是编译器行为。

比如说:

long a = 1;
int b = (int)a;

编译出来的代码是:

IL_000d: stloc.1
IL_000e: ldc.i4.1
IL_000f: conv.i8
IL_0010: stloc.2
IL_0011: ldloc.2
IL_0012: conv.i4
IL_0013: stloc.3

这就是编译器的行为了,编译器认为自己可以处理就不报错了,直接截断作为处理了。

好吧,不能走的太远了,回到is 上。

那么is就可以避免一些运行时候的报错,而不需要用try catch 这种不太优雅的方式。

但是呢,is 是无法去检查自己编写的显示转换或隐式转换的方法,请看VAR:

class Program
{
	static void Main(string[] args)
	{
		object obj = new Student();
		if (obj is Teacher)
		{
			var c = (Teacher)obj;
		}
	}

	public class Student
	{
		
	}

	public class Teacher
	{ 
	}
}

编译出来呢?是下面这样:

.method private hidebysig static 
	void Main (
		string[] args
	) cil managed 
{
	.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
		01 00 01 00 00
	)
	// Method begins at RVA 0x20a4
	// Header size: 12
	// Code size: 30 (0x1e)
	.maxstack 2
	.entrypoint
	.locals init (
		[0] object obj,
		[1] bool,
		[2] class ConsoleApp4.Program/Teacher c
	)

	IL_0000: nop
	IL_0001: newobj instance void ConsoleApp4.Program/Student::.ctor()
	IL_0006: stloc.0
	IL_0007: ldloc.0
	IL_0008: isinst ConsoleApp4.Program/Teacher
	IL_000d: ldnull
	IL_000e: cgt.un
	IL_0010: stloc.1
	IL_0011: ldloc.1
	IL_0012: brfalse.s IL_001d

	IL_0014: nop
	IL_0015: ldloc.0
	IL_0016: castclass ConsoleApp4.Program/Teacher
	IL_001b: stloc.2
	IL_001c: nop

	IL_001d: ret
} // end of method Program::Main

is 转换为il代码就是isinst

这个只能判断继承关系,所以嘛,这个呢,其实也能理解,如果是自己编写了转换方法,哪里自己不知道还要is呢。

那么为啥会出现as呢?

还是性能问题嘛。

v1:

static void Main(string[] args)
{
	object obj = new Student();
	if (obj is Teacher)
	{
		var c = (Teacher)obj;
	}
}

v2:

static void Main(string[] args)
{
	object obj = new Student();
	var c = obj as Teacher;
}

public class Student
{
	
}

public class Teacher
{ 
}

这两者有啥区别呢?道理上运行结果都一致。

但是呢,v2 更优,因为v2检查了一次,而v1检查来了两次。

v2的il代码:

.method private hidebysig static 
	void Main (
		string[] args
	) cil managed 
{
	.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
		01 00 01 00 00
	)
	// Method begins at RVA 0x20a4
	// Header size: 12
	// Code size: 15 (0xf)
	.maxstack 1
	.entrypoint
	.locals init (
		[0] object obj,
		[1] class ConsoleApp4.Program/Teacher c
	)

	IL_0000: nop
	IL_0001: newobj instance void ConsoleApp4.Program/Student::.ctor()
	IL_0006: stloc.0
	IL_0007: ldloc.0
	IL_0008: isinst ConsoleApp4.Program/Teacher
	IL_000d: stloc.1
	IL_000e: ret
} // end of method Program::Main

这里也是使用了isinst,因为isinst本身就是检测并且赋值,再来看下isinst的功效

as 只是运行了stloc.1,弹出赋值给局部变量,多了一个赋值过程,两者性能都差不多,所以呢,不要觉得as 好像多比 is 多个一个转换啥的,其实调用的是同一个语句。

只是觉得以前写的过于潦草,整理下罢了。操作系统篇马上就要出炉,比较生硬,共128篇,简单整理一下。

上一篇:Unity项目性能优化列表


下一篇:ENSP IPV6-over-IPV4