- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
这里是一些用 C# 编写的测试程序:
using System;
struct Foo {
int x;
public Foo(int x) {
this.x = x;
}
public override string ToString() {
return x.ToString();
}
}
class Program {
static void PrintFoo(ref Foo foo) {
Console.WriteLine(foo);
}
static void Main(string[] args) {
Foo foo1 = new Foo(10);
Foo foo2 = new Foo(20);
Console.WriteLine(foo1);
PrintFoo(ref foo2);
}
}
这里是方法 Main 的反汇编编译版本:
.method private hidebysig static void Main (string[] args) cil managed {
// Method begins at RVA 0x2078
// Code size 42 (0x2a)
.maxstack 2
.entrypoint
.locals init (
[0] valuetype Foo foo1,
[1] valuetype Foo foo2
)
IL_0000: ldloca.s foo1
IL_0002: ldc.i4.s 10
IL_0004: call instance void Foo::.ctor(int32)
IL_0009: ldloca.s foo2
IL_000b: ldc.i4.s 20
IL_000d: newobj instance void Foo::.ctor(int32)
IL_0012: stobj Foo
IL_0017: ldloc.0
IL_0018: box Foo
IL_001d: call void [mscorlib]System.Console::WriteLine(object)
IL_0022: ldloca.s foo2
IL_0024: call void Program::PrintFoo(valuetype Foo&)
IL_0029: ret
} // end of method Program::Main
我不明白为什么发出 newobj/stobj 而不是简单的调用 .ctor ?更神秘的是,newobj+stobj 在 32 位模式下被 jit-compiler 优化为一个 ctor 调用,但在 64 位模式下它没有...
更新:
为了澄清我的困惑,以下是我的期望。
值类型声明表达式如
Foo foo = new Foo(10)
应该通过
编译调用实例 void Foo::.ctor(int32)
值类型声明表达式如
Foo foo = default(Foo)
应该通过
编译initobj Foo
在我看来,构造表达式中的临时变量或默认表达式的实例应被视为目标变量,因为这不会导致任何危险行为
try{
//foo invisible here
...
Foo foo = new Foo(10);
//we never get here, if something goes wrong
}catch(...){
//foo invisible here
}finally{
//foo invisible here
}
赋值表达式如
foo = new Foo(10);//foo 在之前某处声明过
应该编译成这样:
.locals init (
...
valuetype Foo __temp,
...
)
...
ldloca __temp
ldc.i4 10
call instance void Foo::.ctor(int32)
ldloc __temp
stloc foo
...
这是我理解 C# 规范所说的方式:
7.6.10.1 Object creation expressions
...
The run-time processing of an object-creation-expression of the form new T(A), where T is class-type or a struct-type and A is an optional argument-list, consists of the following steps:
...
If T is a struct-type:
An instance of type T is created by allocating a temporary local variable. Since an instance constructor of a struct-type is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary.
The instance constructor is invoked according to the rules of function member invocation (§7.5.4). A reference to the newly allocated instance is automatically passed to the instance constructor and the instance can be accessed from within that constructor as this.
我想强调“分配一个临时本地变量”。在我的理解中,newobj 指令假设在堆上创建对象......
在这种情况下,对象创建与其使用方式的依赖性让我很失望,因为 foo1 和 foo2 对我来说看起来完全相同。
最佳答案
首先,您应该阅读我关于这个主题的文章。它没有解决您的特定场景,但它有一些很好的背景信息:
https://ericlippert.com/2010/10/11/debunking-another-myth-about-value-types/
好的,既然您已经阅读过,您就知道 C# 规范规定构造结构实例具有以下语义:
所以当你说:
Foo foo = new Foo(123);
相当于:
Foo foo;
Foo temp = default(Foo);
Foo.ctor(ref temp, 123); // "this" is a ref to a variable in a struct.
foo1 = temp;
现在,您可能会问,既然我们已经拥有一个变量foo
,为什么还要费力地分配一个临时变量呢?就在那里可能是this
:
Foo foo = default(Foo);
Foo.ctor(ref foo, 123);
这种优化称为复制省略。当 C# 编译器和/或抖动确定使用他们的启发式方法时,他们可以执行复制省略,因为这样做总是不可见。在极少数情况下,复制省略会导致程序发生可观察到的变化,在这种情况下,不得使用优化。例如,假设我们有一对整数结构:
Pair p = default(Pair);
try { p = new Pair(10, 20); } catch {}
Console.WriteLine(p.First);
Console.WriteLine(p.Second);
我们期望p
这是(0, 0)
或 (10, 20)
, 从不 (10, 0)
或 (0, 20)
,即使 ctor 中途抛出。也就是说,要么分配给 p
是完全构建的值,或者没有对 p
进行修改根本。这里不能进行复制省略;我们必须制作一个临时文件,将临时文件传递给 ctor,然后将临时文件复制到 p
.
同样,假设我们有这种精神错乱:
Pair p = default(Pair);
p = new Pair(10, 20, ref p);
Console.WriteLine(p.First);
Console.WriteLine(p.Second);
如果 C# 编译器执行复制省略,则 this
和 ref p
都是 p
的别名,这明显不同于如果 this
是临时的别名! ctor 可以观察到 this
的变化导致对 ref p
的更改如果他们给同一个变量起了别名,但如果他们给不同的变量起了别名就不会观察到。
C# 编译器启发式决定对 foo1
执行复制省略但不是 foo2
在你的程序中。它看到有一个 ref foo2
在你的方法中并决定在那里放弃。它可以进行更复杂的分析以确定它不在这些疯狂的别名情况之一中,但事实并非如此。如果有任何机会,无论多么遥远,都可能存在使省略可见的混叠情况,那么便宜且容易做的事情就是跳过优化。它生成 newobj
编码,让抖动决定是否要进行省略。
至于抖动:64位和32位抖动有完全不同的优化器。显然其中一个决定它可以引入 C# 编译器没有的复制省略,而另一个则没有。
关于c# - 为什么 c# 编译器在某些情况下会发出 newobj/stobj 而不是 'call instance .ctor' 来进行结构初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15207683/
我是 Java 新手,这是我的代码, if( a.name == b.name && a.displayname == b.displayname && a.linknam
在下面的场景中,我有一个 bool 值。根据结果,我调用完全相同的函数,唯一的区别是参数的数量。 var myBoolean = ... if (myBoolean) { retrieve
我是一名研究 C++ 的 C 开发人员: 我是否正确理解如果我抛出异常然后堆栈将展开直到找到第一个异常处理程序?是否可以在不展开的情况下在任何 throw 上打开调试器(即不离开声明它的范围或任何更高
在修复庞大代码库中的错误时,我观察到一个奇怪的情况,其中引用的动态类型从原始 Derived 类型更改为 Base 类型!我提供了最少的代码来解释问题: struct Base { // some
我正在尝试用 C# 扩展给定的代码,但由于缺乏编程经验,我有点陷入困境。 使用 Visual Studio 社区,我尝试通过控制台读出 CPU 核心温度。该代码使用开关/外壳来查找传感器的特定名称(即
这可能是一个哲学问题。 假设您正在向页面发出 AJAX 请求(这是使用 Prototype): new Ajax.Request('target.asp', { method:"post", pa
我有以下 HTML 代码,我无法在所有浏览器中正常工作: 我试图在移动到
我对 Swift 很陌生。我如何从 addPin 函数中检索注释并能够在我的 addLocation 操作 (buttonPressed) 中使用它。我正在尝试使用压力触摸在 map 上添加图钉,在两
我设置了一个详细 View ,我是否有几个 Nib 文件根据在 Root View Controller 的表中选择的项目来加载。 我发现,对于 Nibs 的类,永远不会调用 viewDidUnloa
我需要动态访问 json 文件并使用以下代码。在本例中,“bpicsel”和“temp”是变量。最终结果类似于“data[0].extit1” var title="data["+bpicsel+"]
我需要使用第三方 WCF 服务。我已经在我的证书存储中配置了所需的证书,但是在调用 WCF 服务时出现以下异常。 向 https://XXXX.com/AHSharedServices/Custome
在几个 SO 答案(1、2)中,建议如果存在冲突则不应触发 INSERT 触发器,ON CONFLICT DO NOTHING 在触发语句中。也许我理解错了,但在我的实验中似乎并非如此。 这是我的 S
如果进行修改,则会给出org.hibernate.NonUniqueObjectException。在我的 BidderBO 类(class)中 @Override @Transactional(pr
我使用 indexOf() 方法来精细地查找数组中的对象。 直到此刻我查了一些资料,发现代码应该无法正常工作。 我在reducer中尝试了上面的代码,它成功了 let tmp = state.find
假设我有以下表格: CREATE TABLE Game ( GameID INT UNSIGNED NOT NULL, GameType TINYINT UNSIGNED NOT NU
代码: Alamofire.request(URL(string: imageUrl)!).downloadProgress(closure: { (progress) in
我是一名优秀的程序员,十分优秀!