"operator is" 在 C# 中是如何工作的?
How does "operator is" work under the hood in C#?
我一直在深入研究反射和 RTTI 等概念。例如,最近我了解到 dynamic_cast 在 C++ 中是如何工作的。这帮助我理解了为什么它被认为是一个缓慢的操作(通常)。我发现思考 C#(和 .NET)和 C++ 之间的差异非常有帮助。这就是我最终想到 "operator is" 的方式,以及它到底做了什么。它做了什么样的比较?我假设它用 "Type".
做了一些事情
干杯。
7.10.10 The is operator
The is operator is used to dynamically check if the run-time type of an object is compatible with a given type. The result of the operation E is T, where E is an expression and T is a type, is a boolean value indicating whether E can successfully be converted to type T by a reference conversion, a boxing conversion, or an unboxing conversion. The operation is evaluated as follows, after type arguments have been substituted for all type parameters:
• If E is an anonymous function, a compile-time error occurs
• If E is a method group or the null literal, of if the type of E is a reference type or a nullable type and the value of E is null, the result is false.
• Otherwise, let D represent the dynamic type of E as follows:
o If the type of E is a reference type, D is the run-time type of the instance reference by E.
o If the type of E is a nullable type, D is the underlying type of that nullable type.
o If the type of E is a non-nullable value type, D is the type of E.
• The result of the operation depends on D and T as follows:
o If T is a reference type, the result is true if D and T are the same type, if D is a reference type and an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists.
o If T is a nullable type, the result is true if D is the underlying type of T.
o If T is a non-nullable value type, the result is true if D and T are the same type.
o Otherwise, the result is false.
Note that user defined conversions, are not considered by the is operator.
关键部分可能是 "D is the run-time type of the instance reference by E":在 C# 中,每个引用类型都有一个包含该类型的 Type
字段。
所有有效的隐式引用转换都在 6.1.6:
中定义
The implicit reference conversions are:
• From any reference-type to object and dynamic.
• From any class-type S to any class-type T, provided S is derived from T.
• From any class-type S to any interface-type T, provided S implements T.
• From any interface-type S to any interface-type T, provided S is derived from T.
• ...
还有一些,但这些是最重要的转化。
TL;DR; CIL 有提供此功能的特殊指令。
例如我们采用这段代码:
var obj = GetObject();
if (obj is Program)
{
Console.WriteLine("How did we end up here?");
}
else
{
Console.WriteLine("It can't be main class!");
}
GetObject()
returns new object()
所以它不能是入口点的实例 class.
编译后重新创建的代码如下所示:
// Token: 0x0600000D RID: 13 RVA: 0x0000233B File Offset: 0x0000053B
[STAThread]
private static void Main()
{
if (Program.GetObject() is Program)
{
Console.WriteLine("How did we end up here?");
return;
}
Console.WriteLine("It can't be main class!");
}
查看生成的 IL 代码时,我们可以看到:
.method private hidebysig static
void Main () cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (
01 00 00 00
)
// Header Size: 1 byte
// Code Size: 34 (0x22) bytes
.maxstack 8
.entrypoint
/* 0x0000053C 280C000006 */ IL_0000: call object Test.Program::GetObject()
/* 0x00000541 7503000002 */ IL_0005: isinst Test.Program
/* 0x00000546 2C0B */ IL_000A: brfalse.s IL_0017
/* 0x00000548 7208020070 */ IL_000C: ldstr "How did we end up here?"
/* 0x0000054D 283800000A */ IL_0011: call void [mscorlib]System.Console::WriteLine(string)
/* 0x00000552 2A */ IL_0016: ret
/* 0x00000553 7238020070 */ IL_0017: ldstr "It can't be main class!"
/* 0x00000558 283800000A */ IL_001C: call void [mscorlib]System.Console::WriteLine(string)
/* 0x0000055D 2A */ IL_0021: ret
} // end of method Program::Main
名为 isinst
的特殊指令检查您的对象的类型是否等于您提供的类型。
我一直在深入研究反射和 RTTI 等概念。例如,最近我了解到 dynamic_cast 在 C++ 中是如何工作的。这帮助我理解了为什么它被认为是一个缓慢的操作(通常)。我发现思考 C#(和 .NET)和 C++ 之间的差异非常有帮助。这就是我最终想到 "operator is" 的方式,以及它到底做了什么。它做了什么样的比较?我假设它用 "Type".
做了一些事情干杯。
7.10.10 The is operator
The is operator is used to dynamically check if the run-time type of an object is compatible with a given type. The result of the operation E is T, where E is an expression and T is a type, is a boolean value indicating whether E can successfully be converted to type T by a reference conversion, a boxing conversion, or an unboxing conversion. The operation is evaluated as follows, after type arguments have been substituted for all type parameters:
• If E is an anonymous function, a compile-time error occurs
• If E is a method group or the null literal, of if the type of E is a reference type or a nullable type and the value of E is null, the result is false.
• Otherwise, let D represent the dynamic type of E as follows:
o If the type of E is a reference type, D is the run-time type of the instance reference by E.
o If the type of E is a nullable type, D is the underlying type of that nullable type.
o If the type of E is a non-nullable value type, D is the type of E.
• The result of the operation depends on D and T as follows:
o If T is a reference type, the result is true if D and T are the same type, if D is a reference type and an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists.
o If T is a nullable type, the result is true if D is the underlying type of T.
o If T is a non-nullable value type, the result is true if D and T are the same type.
o Otherwise, the result is false.
Note that user defined conversions, are not considered by the is operator.
关键部分可能是 "D is the run-time type of the instance reference by E":在 C# 中,每个引用类型都有一个包含该类型的 Type
字段。
所有有效的隐式引用转换都在 6.1.6:
The implicit reference conversions are:
• From any reference-type to object and dynamic.
• From any class-type S to any class-type T, provided S is derived from T.
• From any class-type S to any interface-type T, provided S implements T.
• From any interface-type S to any interface-type T, provided S is derived from T.
• ...
还有一些,但这些是最重要的转化。
TL;DR; CIL 有提供此功能的特殊指令。
例如我们采用这段代码:
var obj = GetObject();
if (obj is Program)
{
Console.WriteLine("How did we end up here?");
}
else
{
Console.WriteLine("It can't be main class!");
}
GetObject()
returns new object()
所以它不能是入口点的实例 class.
编译后重新创建的代码如下所示:
// Token: 0x0600000D RID: 13 RVA: 0x0000233B File Offset: 0x0000053B
[STAThread]
private static void Main()
{
if (Program.GetObject() is Program)
{
Console.WriteLine("How did we end up here?");
return;
}
Console.WriteLine("It can't be main class!");
}
查看生成的 IL 代码时,我们可以看到:
.method private hidebysig static
void Main () cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = (
01 00 00 00
)
// Header Size: 1 byte
// Code Size: 34 (0x22) bytes
.maxstack 8
.entrypoint
/* 0x0000053C 280C000006 */ IL_0000: call object Test.Program::GetObject()
/* 0x00000541 7503000002 */ IL_0005: isinst Test.Program
/* 0x00000546 2C0B */ IL_000A: brfalse.s IL_0017
/* 0x00000548 7208020070 */ IL_000C: ldstr "How did we end up here?"
/* 0x0000054D 283800000A */ IL_0011: call void [mscorlib]System.Console::WriteLine(string)
/* 0x00000552 2A */ IL_0016: ret
/* 0x00000553 7238020070 */ IL_0017: ldstr "It can't be main class!"
/* 0x00000558 283800000A */ IL_001C: call void [mscorlib]System.Console::WriteLine(string)
/* 0x0000055D 2A */ IL_0021: ret
} // end of method Program::Main
名为 isinst
的特殊指令检查您的对象的类型是否等于您提供的类型。