2017-04-11 73 views
2

由于引入了C#7.0的引用返回功能,并且基于我的理解,像这样的功能需要在编译器上重新布线才能将此引用仅存储在位于堆上的变量上,是否可以存储对堆栈中的变量返回引用,还是新的ref声明确保变量始终存储在堆中?C#7.0中引用返回的值是否存储在堆栈或堆上?

ref int x = ref DoSomething(data); 
// Is the value of x now on the stack or the heap? Or is x stored on the stack as a reference and the value on the heap? 

我的理解是基于this article

最后,CLR确实允许“裁判返回类型”;你理论上可以在 有一个方法“ref int M(){...}”,它返回一个对整数变量 的引用。如果出于某种奇怪的原因我们决定允许在C#中使用 ,我们必须修复编译器和验证器,以便它们确保只有将ref返回到已知在堆中的变量 ,或者已知在堆栈 上比被调用者“降低”。

+0

如果它是一个局部变量,那么它肯定会在栈上 –

+1

@EhsanSajjad如果它只是一个局部变量,那么你不能从'ref'返回方法返回它。 – Servy

+0

你能说一说你说的@Servy是什么意思吗? –

回答

10

这首歌的名字叫做“哈多克斯的眼睛”。 '哦,那是这首歌的名字,是吗?'爱丽丝说,试图感到兴趣。 “不,你不明白,”奈特说,看起来有点烦恼。 '这就是这个名字的名字。这个名字真的是“老年人”。 '然后我应该说'这就是这首歌的名字'?'爱丽丝纠正自己。 “不,你不应该这样做,那是另一回事!这首歌被称为“方式和手段”:但这只是它所谓的,你知道的! '那么,那首歌是什么,然后呢?'爱丽丝说,此时此刻他完全被困惑了。 “我刚才说到,”奈特说。 “这首歌真的是”坐在大门上“:这首曲子是我自己的发明。”

歌曲,歌曲名称,名称叫什么以及歌曲名称显然都不同。


还有,你一定不要混淆了三样东西:

  • 参考变量的位置 - 别名
  • 被提及变量的位置 - 别名变量。
  • 如果被引用变量是引用类型,则引用该对象的位置(如果有)。

ref int x = ref DoSomething(data); 

是x的值现在在堆栈或堆上?

表达x别名另一个变量

该变量有一个值。

假设别名变量是一个值类型。是别名变量,因此它的值,临时池(又名“堆栈”)或长期池(“堆”)?我们不知道。我们也不在乎。我们知道,无论哪一个,我们都保证这个变量现在是活着的。

但是我们可以做一个猜测:裁判返回变量通常别名堆上分配的变量,因为我们确实知道他们还活着

假设别名变量是引用类型。是参考存储在堆栈或堆?同样,我们也不知道,出于同样的原因。是在堆栈或堆上的参考的参考?它在堆上,或者它是空的;我们保证。

或者x被存储在栈上作为引用和堆上的值?

通过x你的意思的变量x别名,或者你的意思是当地x本身?本地本身存储在堆栈或寄存器中;它从来没有被提升到封闭类的领域。变量别名可能在任何地方,但如上所述,可能在堆上。

+0

我爱你的答案。但是,C#7如何确保变量在创建别名时仍处于活动状态?我目前无法尝试,但是在同样的方法工作中不返回对本地变量的引用(在堆栈上)? – IllidanS4

+0

@ IllidanS4:编译器必须跟踪每个ref的来源和发生的情况。返回一个我们知道的ref是本地的ref是不合法的。 –

+0

它可以在所有情况下这样做吗?如果在另一个已编译的程序集中有ref-returning ref-taking方法,它只返回它提供的引用?即使在这种情况下编译器是否会检查该方法? – IllidanS4

7

ref int x是语法sugar int* x,这是变量在运行时的行为方式。当你将一个变量作为参数传递给一个方法并且该参数声明为ref时,调用者传递一个指向该变量的指针时,会得到与你完全相同的行为。指针不关心值的存储位置,实际上可以存储在任何地方。请注意,您无法传递属性,它没有存储空间。

他们不得不在C#v7中做的不平凡的事情是确保你不会无意中创建一个dangling pointer bug。臭名昭着的像C和C++这样的语言,在这些语言中未定义的行为不需要编译器生成诊断。许多带有该错误的程序似乎运行得很好,直到程序中看起来微不足道的变化(添加函数调用)破坏指向的值。

实际上并不难做afaik,被调用方法的局部变量是麻烦制造者。方法返回后它们不再存在,因此引用变为无效。这个特定的场景会在C#v7中给你一个编译错误。但是,如果局部变量是调用者之一,并且它通过参数传递,那么该变量仍然存在。