pae*_*bal 15 c# generics boxing
可能重复:
结构,接口和拳击
来自MSDN:http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx
Boxing是将值类型转换为类型对象或由此值类型实现的任何接口类型的过程.
但是通用接口呢?
例如,int
派生自IComparable
和IComparable<int>
.
假设我有以下代码:
void foo(IComparable value) { /* etc. */ }
void bar(IComparable<T> value) { /* etc. */ }
void gizmo()
{
int i = 42;
bar(i); // is `i` boxed? I'd say YES
foo(i); // is `i` boxed? I fear it is (but I hope for NO)
}
Run Code Online (Sandbox Code Playgroud)
是bar
(或任何采用非通用接口的功能)意味着会有拳击吗?
是foo
(或任何函数采用类型的通用接口)意味着将有拳击?
谢谢.
sup*_*cat 20
无论何时将结构体转换为接口,都会将其装箱.IComparable <T>的目的是允许类似的东西:
void bar<T>(T value) where T : IComparable<T> { /* etc. */ }
Run Code Online (Sandbox Code Playgroud)
当以这种方式使用时,struct将作为结构(通过泛型类型参数)而不是作为接口传递,因此不必加框.请注意,根据结构的大小,有时可能更好地通过值传递,有时通过引用传递,但当然如果使用现有的接口,如IComparable,则必须按接口要求传递.
首先,关于值类型,引用类型和装箱的简短(可能是不完整的)入门.
您可以判断某些内容是值类型,因为函数中所做的更改不会在函数外部保留.调用函数时会复制对象的值,并在该函数的末尾抛弃该值.
您可以判断某些内容是引用类型,因为函数中所做的更改将保留在函数外部.调用函数时不会复制对象的值,并且在该函数结束后存在该值.
如果装箱,则会制作一份副本,并在参考类型中就座.它实际上从值类型更改为引用类型.
请注意,这一切都适用于实例化状态,即任何非静态成员数据.静态成员不是实例状态,与引用类型,值类型或装箱无关.不使用实例化状态的方法和属性(例如,仅使用局部变量或静态成员数据的方法和属性)在引用类型,值类型或发生装箱时的操作方式不同.
有了这些知识,这就是我们如何证明在将结构转换为接口时发生装箱(通用或非通用):
using System;
interface ISomeInterface<T>
{
void Foo();
T MyValue { get; }
}
struct SomeStruct : ISomeInterface<int>
{
public void Foo()
{
this.myValue++;
}
public int MyValue
{
get { return myValue; }
}
private int myValue;
}
class Program
{
static void SomeFunction(ISomeInterface<int> value)
{
value.Foo();
}
static void Main(string[] args)
{
SomeStruct test1 = new SomeStruct();
ISomeInterface<int> test2 = test1;
// Call with struct directly
SomeFunction(test1);
Console.WriteLine(test1.MyValue);
SomeFunction(test1);
Console.WriteLine(test1.MyValue);
// Call with struct converted to interface
SomeFunction(test2);
Console.WriteLine(test2.MyValue);
SomeFunction(test2);
Console.WriteLine(test2.MyValue);
}
}
Run Code Online (Sandbox Code Playgroud)
输出如下所示:
0
0
1
2
这意味着只有在进行转换时才会发生装箱:
我不会打扰这里复制所有的代码,但如果你改变ISomeInterface<T>
对ISomeInterface
,您仍然有相同的行为.
我对通用接口和装箱/拆箱的困惑来自于我知道C#generics使我们能够生成更高效的代码.
例如,事实int
农具IComparable<T>
和 IComparable
对我意味着什么:
IComparable
是用于旧的,前泛型代码,但意味着装箱/拆箱IComparable<T>
是用于泛型启用代码,据说避免装箱/拆箱Eric Lippert的评论尽可能简单,明确和直接:
通用接口类型是接口类型.他们没有什么特别的,可以神奇地防止拳击
从现在开始,我毫无疑问地知道将一个结构体转换为界面将意味着拳击.
但那么,IComparable<T>
应该如何更有效地工作IComparable
呢?
这就是supercat的答案(由Lasse V. Karlsen编辑)向我指出仿制药比我想象的更像C++模板:
IComparable的目的是允许以下内容:
void bar<T>(T value) where T : IComparable<T> { /* etc. */ }
Run Code Online (Sandbox Code Playgroud)
这与以下内容截然不同:
void bar(IComparable<T> value) { /* etc. */ }
Run Code Online (Sandbox Code Playgroud)
甚至:
void bar(IComparable value) { /* etc. */ }
Run Code Online (Sandbox Code Playgroud)
我的猜测是,对于第一个原型,运行时将为每个类型生成一个函数,因此,在处理结构时避免装箱问题.
然而,对于第二个原型,运行时将仅生成以接口作为参数的函数,因此,当T是结构时进行装箱.第三个函数只是将结构框,不多也不少.
(我猜这是与Java类型擦除泛型实现相比,C#泛型与C#结构相结合显示其优越性的地方.)
Merlyn Morgan-Graham的回答为我提供了一个我将在家里玩的测试的例子.一旦我得到有意义的结果,我就会完成这个摘要(我想我会尝试使用pass-by-reference语义来看看它是如何工作的......)