con*_*low 82 c# boxing value-type
我正在尝试收集C#中发生装箱的所有情况:
将值类型转换为System.Object
类型:
struct S { }
object box = new S();
Run Code Online (Sandbox Code Playgroud)将值类型转换为System.ValueType
类型:
struct S { }
System.ValueType box = new S();
Run Code Online (Sandbox Code Playgroud)将枚举类型的值转换为System.Enum
类型:
enum E { A }
System.Enum box = E.A;
Run Code Online (Sandbox Code Playgroud)将值类型转换为接口引用:
interface I { }
struct S : I { }
I box = new S();
Run Code Online (Sandbox Code Playgroud)在C#字符串连接中使用值类型:
char c = F();
string s1 = "char value will box" + c;
Run Code Online (Sandbox Code Playgroud)
注意:char
类型的常量在编译时连接在一起
注意:由于6.0版的C#编译器优化串联涉及bool
,char
,IntPtr
,UIntPtr
类型
从值类型实例方法创建委托:
struct S { public void M() {} }
Action box = new S().M;
Run Code Online (Sandbox Code Playgroud)在值类型上调用未重写的虚方法:
enum E { A }
E.A.GetHashCode();
Run Code Online (Sandbox Code Playgroud)在is
表达式下使用C#7.0常量模式:
int x = …;
if (x is 42) { … } // boxes both 'x' and '42'!
Run Code Online (Sandbox Code Playgroud)拳击C#元组类型转换:
(int, byte) _tuple;
public (object, object) M() {
return _tuple; // 2x boxing
}
Run Code Online (Sandbox Code Playgroud)object
具有值类型默认值的类型的可选参数:
void M([Optional, DefaultParameterValue(42)] object o);
M(); // boxing at call-site
Run Code Online (Sandbox Code Playgroud)检查无约束泛型类型的值null
:
bool M<T>(T t) => t != null;
string M<T>(T t) => t?.ToString(); // ?. checks for null
M(42);
Run Code Online (Sandbox Code Playgroud)
注意:这可能在某些.NET运行时由JIT优化
struct
使用is
/ as
运算符键入无约束或泛型类型的测试值:
bool M<T>(T t) => t is int;
int? M<T>(T t) => t as int?;
IEquatable<T> M<T>(T t) => t as IEquatable<T>;
M(42);
Run Code Online (Sandbox Code Playgroud)
注意:这可能在某些.NET运行时由JIT优化
你知道更多的拳击,也许是隐藏的情况吗?
Mot*_*ked 41
这是一个很好的问题!
拳击的发生只有一个原因:当我们需要对值类型的引用时.您列出的所有内容都属于此规则.
例如,由于object是引用类型,因此将值类型转换为object需要引用值类型,这会导致装箱.
如果要列出每个可能的场景,还应包括派生词,例如从返回对象的方法或接口类型返回值类型,因为这会自动将值类型转换为对象/接口.
顺便说一句,你敏锐地识别出的字符串连接案例也是从强制转换为对象.+运算符由编译器转换为对String的Concat方法的调用,该方法接受您传递的值类型的对象,因此转换为对象并因此发生装箱.
这些年来,我一直建议开发人员记住拳击的唯一原因(我在上面指定),而不是记住每一个案例,因为列表很长很难记住.这也促进了解编译器为我们的C#代码生成的IL代码(例如+ on string会产生对String.Concat的调用).当您怀疑编译器生成什么以及是否发生装箱时,您可以使用IL反汇编程序(ILDASM.exe).通常你应该寻找盒操作码(即使IL不包括盒操作码,也可能发生装箱只有一种情况,下面有更多细节).
但我确实同意一些拳击事件不太明显.您列出了其中之一:调用值类型的非重写方法.事实上,由于另一个原因,这一点不太明显:当你检查IL代码时,你没有看到框操作码,而是约束操作码,所以即使在IL中,拳击也不明显!我不会详细说明为什么要阻止这个答案变得更长......
不太明显的装箱的另一种情况是从结构中调用基类方法时.例:
struct MyValType
{
public override string ToString()
{
return base.ToString();
}
}
Run Code Online (Sandbox Code Playgroud)
这里重写了ToString,因此在MyValType上调用ToString将不会生成装箱.但是,实现调用基本ToString并导致装箱(检查IL!).
顺便说一句,这两个非显而易见的拳击场景也来自上面的单一规则.在值类型的基类上调用方法时,必须有this关键字引用的内容.由于值类型的基类是(总是)引用类型,因此this关键字必须引用引用类型,因此我们需要引用值类型,因此由于单个规则而发生限制.
这是我在线.NET课程的一个直接链接,详细讨论拳击:http://motti.me/mq
如果你只对更高级的拳击场景感兴趣,那么这里有一个直接的链接(虽然上面的链接会在你讨论更基本的东西时带你到那里):http://motti.me/mu
我希望这有帮助!
Motti
在值类型上调用非虚拟GetType()方法:
struct S { };
S s = new S();
s.GetType();
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4007 次 |
最近记录: |