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

时间:2016-11-27 11:47:35

标签: c# properties ref

假设你有这样的课程

public class Foo
{
   public int Bar { get; set; } = 42;
}

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

  

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

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

var foo = new Foo();
foo.Bar++;

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

var foo = new Foo();
int tmp = foo.get_Bar();
tmp++;
foo.set_Bar(tmp);

所以从理论上讲,编译器可以为ref做类似的事情,如:

var foo = new Foo();
int tmp = foo.get_Bar();
DoSomething(ref tmp);
foo.set_Bar(tmp);

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

1 个答案:

答案 0 :(得分:3)

就像HansPassant所说,这是C#团队在编写C#规范时所做的设计决定,所以你必须问其中一个才能得到正确的答案。

但是,如果我冒险猜测,那么使用ref传递属性的编译器魔法数量将导致在幕后发生足够的不明显操作,从而使解决方案不合需要。例如,如何递增/递减属性当前正如您所说:程序将属性的支持字段的值分配给临时变量,执行操作,并将结果重新分配给属性。这是一个简单的过程,没有包含任何困难的概念。

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

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

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

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

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