在C#中,“操作员”是如何工作的?

时间:2019-12-06 08:42:57

标签: c# rtti

我一直在更深入地研究反射和RTTI等概念。例如,最近我了解了dynamic_cast在C ++中如何工作。这帮助我了解了为什么将其视为运行缓慢(通常)。我发现思考C#(和.NET)与C ++之间的区别非常有帮助。这就是我最终想到“操作员是”以及它到底在做什么的方式。它进行什么样的比较?我认为它与“类型”有关。

干杯。

2 个答案:

答案 0 :(得分:2)

来自C# language specification

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是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.
•   ...

还有更多,但这些是最重要的转换。

答案 1 :(得分:1)

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()返回new object(),因此它不能是入口点类的实例。

编译后的重新创建的代码如下所示:

// 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的特殊指令检查对象的类型是否等于您提供的类型。