【问题标题】:Why are increment operators possible on properties while ref is not为什么在属性上可以使用增量运算符,而 ref 不是
【发布时间】:2016-11-27 11:47:34
【问题描述】:

假设你有这样的课程

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# 团队的设计决定?

【问题讨论】:

  • 这实际上是 VB.NET 编译器为使此类代码编译所做的工作。它是一种旨在友好的语言,C# 旨在纯粹。如果纯度与隐藏可能很昂贵的 setter 调用不太兼容,则会在错误的位置生成异常,可能会生成难以诊断的编译错误或导致别名问题。然而,++ 运算符不是很纯粹,并且以不止一种方式绕过规则。例如,您可以在不进行强制转换的情况下增加 byte。他们决定让它实用。选择。
  • @HansPassant 如果您可以将您的评论详细说明为答案,我会接受它
  • @Abion47 我不认为这是重复的,因为您链接的问题没有解决增量/减量运算符。
  • 我无法详细说明,我从未被邀请参加 C# 团队设计会议。所以用户需要事实而不是猜测,他们也应该这样做。 Eric Lippert 可能会出现,尽管在做出决定时他也不在身边。您的问题无法回答,得到回答也不是那么重要,因此不会造成重大损失。

标签: c# properties ref


【解决方案1】:

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-09
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2014-05-19
    • 2013-10-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多