c# – 当作为具有接口约束的通用参数传递时,值类型是否装箱?

(作为回答这个问题的研究结果,我(我想我已经!)确定答案是“不”.但是,我不得不在几个不同的地方看看这个,所以我觉得还有这个问题很有价值.但如果社区投票结束,我不会感到沮丧.)

例如:

void f<T>(T val) where T : IComparable
{
   val.CompareTo(null);
}

void g()
{
   f(4);
}

4盒装?我知道明确地将值类型转换为它实现触发装箱的接口:

((IComparable)4).CompareTo(null); // The Int32 "4" is boxed

我不知道的是,将值类型作为具有接口约束的泛型参数传递是否等于执行强制转换 – 语言“其中T是IC可压缩”类型建议强制转换,但只是将T转换为IComparable似乎它会破坏通用的整个目的!

为了澄清,我想确保在上面的代码中没有发生这些事情:

>当g调用f(4)时,4被强制转换为IComparable,因为f的参数类型存在IComparable约束.
>假设(1)没有发生,在f内,val.CompareTo(null)不会将val从Int32转换为IComparable以调用CompareTo.

但我想了解一般情况;不仅仅是内注和IC可兼容性会发生什么.

现在,如果我将以下代码放入LinqPad:

void Main()
{
    ((IComparable)4).CompareTo(null);
    f(4);
}

void f<T>(T val) where T : IComparable
{
   val.CompareTo(null);
}

然后检查生成的IL:

IL_0001:  ldc.i4.4    
IL_0002:  box         System.Int32
IL_0007:  ldnull      
IL_0008:  callvirt    System.IComparable.CompareTo
IL_000D:  pop         
IL_000E:  ldarg.0     
IL_000F:  ldc.i4.4    
IL_0010:  call        UserQuery.f

f:
IL_0000:  nop         
IL_0001:  ldarga.s    01 
IL_0003:  ldnull      
IL_0004:  constrained. 01 00 00 1B 
IL_000A:  callvirt    System.IComparable.CompareTo
IL_000F:  pop         
IL_0010:  ret  

很明显,拳击发生在预期的显式演员阵容中,但是在f本身*或者在Main的调用站点中没有明显的拳击.这是个好消息.但是,这也只是一种类型的一个例子.所有情况都可以假设这种缺乏拳击的东西吗?

* This MSDN article讨论了受约束的前缀和状态,只要在类型本身(而不是基类)上实现被调用的方法,与callvirt一起使用它就不会触发值类型的装箱.我不确定的是当我们到达这里时类型是否仍然是值类型.

最佳答案
正如你已经想到的那样,当一个struct传递给泛型方法时,它不会被装箱.

Runtime为每个“Type Argument”创建新方法.当您使用值类型调用泛型方法时,实际上是在调用为​​各个值类型创建的专用方法.所以不需要拳击.

当调用未在结构类型中直接实现的接口方法时,将发生装箱. Spec在这里调用它:

If thisType is a value type and thisType does not implement method
then ptr is dereferenced, boxed, and passed as the ‘this’ pointer to
the callvirt method instruction.

This last case can occur only when method was defined on Object,
ValueType, or Enum and not overridden by thisType. In this case, the
boxing causes a copy of the original object to be made. However,
because none of the methods of Object, ValueType, and Enum modify the
state of the object, this fact cannot be detected.

因此,只要您明确[1]在结构本身中实现接口成员,就不会发生装箱.

How, when and where are generic methods made concrete?

1.不要与Explicit接口实现混淆.这就是说你的接口方法应该在struct本身而不是它的基本类型中实现.

转载注明原文:c# – 当作为具有接口约束的通用参数传递时,值类型是否装箱? - 代码日志