我有类似这样的结构
struct MyStructure
:IFoo
{
}
和这样的方法:
public BarThisFoo(IFoo a)
{
}
我的问题是将结构传递给该方法的“框”结构,从而导致垃圾分配?
附录: 在有人说之前,垃圾收集在这个应用程序中不是免费的,它实际上对垃圾收集非常敏感,所以免费分配代码很重要。
答案 0 :(得分:8)
为避免装箱,你可以使用具有约束的泛型:
struct MyStructure
:IFoo
{
}
public void BarThisFoo<T>(T a) where T : IFoo
{
}
参见J. Richter CLR via C#,第2版,第14章:接口,关于泛型和接口约束的部分。
修改强>
示例代码
using System;
using System.Collections;
interface IFoo {
void Foo();
}
struct MyStructure : IFoo {
public void Foo() {
}
}
public static class Program {
static void BarThisFoo<T>(T t) where T : IFoo {
t.Foo();
}
static void Main(string[] args) {
MyStructure s = new MyStructure();
BarThisFoo(s);
}
}
方法Main的IL代码不包含任何框指令:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 15 (0xf)
.maxstack 1
.locals init ([0] valuetype MyStructure s)
IL_0000: ldloca.s s
IL_0002: initobj MyStructure
IL_0008: ldloc.0
IL_0009: call void Program::BarThisFoo<valuetype MyStructure>(!!0)
IL_000e: ret
} // end of method Program::Main
答案 1 :(得分:5)
是的,确实如此。每当您转换时都会发生拳击:
System.ValueType
参考的值类型System.Enum
参考显然,这是案例III。您可以阅读更详尽的示例 here 。
答案 2 :(得分:5)
正如其他人所指出的那样,是的,将结构转换为它实现的接口是一个拳击。更重要的不是问题的答案,而是你能够自己回答。如果使用ILDASM反汇编测试应用程序,您将看到编译器在转换时生成“box”指令。现在下次你有关于拳击的问题时,你可以自己写一个测试程序,反汇编,然后你就会知道。
顺便提一下,请注意,如果在结构上隐式实现的接口方法上调用方法,则不会发生装箱:
struct S : IFoo { public void Foo() { ...
...
myS.Foo(); // no boxing
((IFoo)myS).Foo(); // boxing
这与可变值类型的接口方法特别相关;请记住,如果您正在改变盒装值类型,那么您将改变框中的值,而不是最初包含盒装值的变量。 myS.Foo()和((IFoo)myS).Foo()可以有不同的语义这一事实是可变值类型纯粹是邪恶的另一个原因,应该避免。
答案 3 :(得分:1)
是。将值类型转换为它符合的接口类型将对值进行装箱。为了将值类型视为对象,需要拳击操作。它将必要的标题(包括vtable)添加到值中,以便您可以在其上调用接口方法。盒装值作为任何托管对象进行垃圾收集。
答案 4 :(得分:1)
Boxing是将值类型转换为类型对象或由此值类型实现的任何接口类型的过程。