C# 泛型约束如何防止使用隐式实现的接口装箱值类型?
我的问题与此有点关联: 然而,我的问题是编译器如何启用泛型约束以消除装箱显式实现接口的值类型的需要 我想我的问题可以归结为两个部分:C# 泛型约束如何防止使用隐式实现的接口装箱值类型?,c#,.net,generics,interface,boxing,C#,.net,Generics,Interface,Boxing,我的问题与此有点关联: 然而,我的问题是编译器如何启用泛型约束以消除装箱显式实现接口的值类型的需要 我想我的问题可以归结为两个部分: 在访问显式实现的接口成员时,需要装箱值类型的幕后CLR实现发生了什么,以及 删除此需求的通用约束会发生什么情况 一些示例代码: internal struct TestStruct : IEquatable<TestStruct> { bool IEquatable<TestStruct>.Equals(TestStruct oth
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));
}
}
内部结构TestStruct:IEquatable
{
bool IEquatable.Equals(TestStruct其他)
{
返回true;
}
}
内部类测试器类
{
//方法
公共静态布尔等于(T arg1,T arg2),其中T:i可满足
{
返回arg1.Equals(arg2);
}
公共静态无效运行()
{
TestStruct t1=新的TestStruct();
TestStruct t2=新的TestStruct();
Assert(((IEquatable)t1).Equals(t2));
Assert(AreEqual(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
}
}
.class私有顺序ansi sealed beforefieldinit TestStruct
扩展[mscorlib]System.ValueType
实现[mscorlib]系统。IEquatable`1
{
.method private Hidebysing newslot虚拟最终实例bool System.IEquatable.Equals(valuetype TestStruct other)cil托管
{
.override[mscorlib]System.IEquatable`1::Equals
.maxstack 1
.init(
[0]bool CS$1$0000)
L_0000:没有
L_0001:ldc.i4.1
L_0002:stloc.0
L_0003:br.s L_0005
L_0005:ldloc.0
L_0006:ret
}
}
.class专用自动ansi BEFOREFILDEINIT TESTERSCLASS
扩展[mscorlib]System.Object
{
.method public hidebysing specialname rtspecialname实例void.ctor()cil托管
{
.maxstack 8
L_0000:ldarg.0
L_0001:调用实例void[mscorlib]System.Object::.ctor()
L_0006:ret
}
.method公共隐藏静态布尔值相等(!!T arg1,!!T arg2)cil管理
{
.maxstack 2
.init(
[0]bool CS$1$0000)
L_0000:没有
L_0001:ldarga.s arg1
L_0003:ldarg.1
L_0004:受约束!!T
L_000a:callvirt实例bool[mscorlib]System.IEquatable`1::Equals(!0)
L_000f:stloc.0
L_0010:br.s L_0012
L_0012:ldloc.0
L_0013:ret
}
.method public hidebysing static void Run()cil managed
{
.maxstack 2
.init(
[0]valuetype TestStruct t1,
[1] valuetype TestStruct t2,
[2] 布尔(阿雷平)
L_0000:没有
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实例bool[mscorlib]System.IEquatable`1::Equals(!0)
L_001d:stloc.2
L_001e:ldloc.2
L_001f:调用void[System]System.Diagnostics.Debug::Assert(bool)
L_0024:没有
L_0025:ldloc.0
L_0026:ldloc.1
L_0027:调用bool TesterClass::AreEqual(!!0,!!0)
L_002c:stloc.2
L_002d:ldloc.2
调用void[System]System.Diagnostics.Debug::Assert(bool)
L_0033:没有
L_0034:ret
}
}
键调用受约束!!T而不是box TestStruct
,但在这两种情况下,后续调用仍然是callvirt
因此,我不知道进行虚拟调用所需的装箱操作是什么,而且我特别不理解如何使用约束为值类型的泛型来消除装箱操作的需要
我提前感谢大家…我想你需要使用
- 反射镜
- ildasm/monodis
当然,您可以查看CLR(ECMA)的规范和/或C#编译器的源()泛型约束仅提供编译时检查,以确保将正确的类型传递到方法中。最终结果总是编译器生成接受运行时类型的适当方法:
public struct Foo : IFoo { }
public void DoSomething<TFoo>(TFoo foo) where TFoo : IFoo
{
// No boxing will occur here because the compiler has generated a
// statically typed DoSomething(Foo foo) method.
}
删除泛型约束只会停止编译时检查是否将正确的类型传递给方法
然而,我的问题是编译器如何启用泛型约束以消除装箱显式实现接口的值类型的需要
“编译器”并不清楚你指的是抖动还是C#编译器。C#编译器通过在虚拟调用上发出受限前缀来实现这一点。有关详细信息,请参阅
在访问显式实现的接口成员时,需要装箱值类型的幕后CLR实现发生了什么
被调用的方法是否是显式实现的接口成员并不特别相关。一个更普遍的问题是,为什么任何虚拟调用都需要对值类型进行装箱 人们通常认为虚拟调用是对虚拟函数表中方法指针的间接调用。这并不是CLR中接口调用的工作方式,但对于本文的讨论来说,这是一个合理的心智模型 如果虚拟方法就是这样被调用的,那么vtable从何而来?值类型中没有vtable。值类型仅在其存储中具有其值。装箱创建对对象的引用,该对象具有vtable设置,指向所有值类型的虚拟方法。(我再次提醒您,接口调用并不完全是这样工作的,但这是一种很好的思考方式。) 通用公司会发生什么
public void DoSomething(IFoo foo)
{
// Boxing occurs here as Foo is cast to a reference type of IFoo.
}