Мой вопрос несколько связан с этим: Явно реализованный интерфейс и общие ограничения.
Мой вопрос, однако, заключается в том, как компилятор позволяет общему ограничению устранять необходимость в боксировании типа значения, который явно реализует интерфейс.
Я думаю, мой вопрос сводится к двум частям:
-
Что происходит с реализацией CLR за кадром, которая требует ввода типа значения при доступе к явно реализованному члену интерфейса и
-
Что происходит с общим ограничением, которое устраняет это требование?
Пример кода:
internal struct TestStruct : IEquatable<TestStruct>
{
bool IEquatable<TestStruct>.Equals(TestStruct other)
{
return true;
}
}
internal class TesterClass
{
// Methods
public static bool AreEqual<T>(T arg1, T arg2) where T: IEquatable<T>
{
return arg1.Equals(arg2);
}
public static void Run()
{
TestStruct t1 = new TestStruct();
TestStruct t2 = new TestStruct();
Debug.Assert(((IEquatable<TestStruct>) t1).Equals(t2));
Debug.Assert(AreEqual<TestStruct>(t1, t2));
}
}
И результат IL:
.class private sequential ansi sealed beforefieldinit TestStruct
extends [mscorlib]System.ValueType
implements [mscorlib]System.IEquatable`1<valuetype TestStruct>
{
.method private hidebysig newslot virtual final instance bool System.IEquatable<TestStruct>.Equals(valuetype TestStruct other) cil managed
{
.override [mscorlib]System.IEquatable`1<valuetype TestStruct>::Equals
.maxstack 1
.locals init (
[0] bool CS$1$0000)
L_0000: nop
L_0001: ldc.i4.1
L_0002: stloc.0
L_0003: br.s L_0005
L_0005: ldloc.0
L_0006: ret
}
}
.class private auto ansi beforefieldinit TesterClass
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: call instance void [mscorlib]System.Object::.ctor()
L_0006: ret
}
.method public hidebysig static bool AreEqual<([mscorlib]System.IEquatable`1<!!T>) T>(!!T arg1, !!T arg2) cil managed
{
.maxstack 2
.locals init (
[0] bool CS$1$0000)
L_0000: nop
L_0001: ldarga.s arg1
L_0003: ldarg.1
L_0004: constrained !!T
L_000a: callvirt instance bool [mscorlib]System.IEquatable`1<!!T>::Equals(!0)
L_000f: stloc.0
L_0010: br.s L_0012
L_0012: ldloc.0
L_0013: ret
}
.method public hidebysig static void Run() cil managed
{
.maxstack 2
.locals init (
[0] valuetype TestStruct t1,
[1] valuetype TestStruct t2,
[2] bool areEqual)
L_0000: nop
L_0001: ldloca.s t1
L_0003: initobj TestStruct
L_0009: ldloca.s t2
L_000b: initobj TestStruct
L_0011: ldloc.0
L_0012: box TestStruct
L_0017: ldloc.1
L_0018: callvirt instance bool [mscorlib]System.IEquatable`1<valuetype TestStruct>::Equals(!0)
L_001d: stloc.2
L_001e: ldloc.2
L_001f: call void [System]System.Diagnostics.Debug::Assert(bool)
L_0024: nop
L_0025: ldloc.0
L_0026: ldloc.1
L_0027: call bool TesterClass::AreEqual<valuetype TestStruct>(!!0, !!0)
L_002c: stloc.2
L_002d: ldloc.2
L_002e: call void [System]System.Diagnostics.Debug::Assert(bool)
L_0033: nop
L_0034: ret
}
}
Ключевой вызов constrained !!T
вместо box TestStruct
, но последующий вызов по-прежнему callvirt
в обоих случаях.
Поэтому я не знаю, что это такое с боксом, который требуется для создания виртуального вызова, и я особенно не понимаю, как использование общего ограничения, связанного с типом значения, устраняет необходимость в операции бокса.
Я благодарю всех заблаговременно...