为什么涉及属性时可能会增加运算符



说你有这样的类

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

如果您尝试以ref参数的方式传递该属性,则编译器会发出错误

cs0206不得将属性或索引器作为淘汰或裁判传递 参数

这是可以理解的,因为在实践中,上述示例中的属性已编译为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);

编译器不这样做的技术原因是否有技术原因?

就像汉斯帕特(Hanspassant)所说的那样,这是C#团队编写C#规范时做出的设计决定,因此您必须询问其中一个才能获得适当的答案。p>,如果我要猜测一个猜测,那就是,用ref通过属性的编译器魔术数量会导致足够的不太明显的操作在幕后发生,从而使解决方案不受欢迎。例如,当前属性的增量/减小属性的工作方式是:该程序将属性的备份字段的值分配给临时变量,执行操作并将结果重新分配给属性。这是一个直接的过程,不包含任何困难的概念。

在幕后做同样的事情以通过ref传递属性,但是,该过程变得更加涉及。当ref传递值类型时,通过参数传递的实际值是指向值类型变量的指针。但是,要为财产做这件事,您必须做类似于第二个示例的事情。这将导致将临时变量(而不是属性本身)的地址传递给该方法。这种行为可能会导致某些试图以某些方式操纵ref参数的人的不可预见和难以理解的后果。

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

编辑:我发生的另一个原因,对于一个字段,该方法中的任何操作都会反映在字段本身上。这些操作可以通过其他线程可以看到,从而可以在该方法执行过程中访问该字段(关于并发现场可访问性的最佳实践)。

对于一个参数,在方法返回并重新复制该方法之前,该方法内发生的任何更改都不会看到。这将导致字段和属性之间的行为不一致,其原因将不易显而易见。

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

最新更新