为什么增量运算符可以在属性上而ref不是

Tho*_*ser 5 c# properties ref

假设你有这样的课程

public class Foo
{
   public int Bar { get; set; } = 42;
}
Run Code Online (Sandbox Code Playgroud)

如果您尝试将该属性作为ref参数传递,则编译器会发出错误

CS0206属性或索引器不能作为out或ref参数传递

因为在实践中在上面的例子中财产被编译成这是可以理解get_Bar()set_Bar()方法.但是如果你在属性上使用增量运算符,比如

var foo = new Foo();
foo.Bar++;
Run Code Online (Sandbox Code Playgroud)

它按预期工作.为此,编译器需要生成类似这样的伪代码:

var foo = new Foo();
int tmp = foo.get_Bar();
tmp++;
foo.set_Bar(tmp);
Run Code Online (Sandbox Code Playgroud)

所以理论上编译器可以做类似的事情ref:

var foo = new Foo();
int tmp = foo.get_Bar();
DoSomething(ref tmp);
foo.set_Bar(tmp);
Run Code Online (Sandbox Code Playgroud)

有没有技术上的原因,编译器不这样做,或者这只是C#团队的设计决定?

Abi*_*n47 3

正如 HansPassant 所说,这是 C# 团队在编写 C# 规范时做出的设计决定,因此您必须询问其中一个人才能得到正确的答案。

不过,如果我大胆猜测一下,传递属性的编译器魔法量会ref导致幕后发生足够多的不明显操作,从而使解决方案变得不可取。例如,当前递增/递减属性的工作方式如您所说:程序将属性的支持字段的值分配给临时变量,执行操作,然后将结果重新分配给属性。这是一个简单的过程,不包含任何困难的概念。

然而,要在幕后执行相同的魔法来传递属性ref,该过程会变得更加复杂。当通过 传递值类型时ref,通过参数传递的实际值是指向值类型变量的指针。但是,要对属性执行此操作,您必须执行类似于第二个示例的操作。这将导致临时变量的地址,而不是属性本身,被传递给方法。对于试图以某些方式操纵参数的人来说,这种行为可能会导致一些不可预见且难以理解的后果ref

所以我的猜测是,增量运算符很容易包装,因为它只处理值,而关键字ref则更复杂,因为它还必须担心范围和内存地址。

编辑:我想到的另一个原因是,对于一个字段,被调用方法内的任何操作都会反映在该字段本身上。这些操作可以被其他线程看到,并在方法执行期间访问该字段(除了关于并发字段可访问性的最佳实践)。

然而,对于参数来说,方法内部发生的任何更改在方法返回并将值复制回来之前都是不可见的。这将导致字段和属性之间的行为不一致,其原因并不明显。

(我个人认为这是不支持ref属性的更可能的原因。)