C#属性和ref参数,为什么没糖?

BCS*_*BCS 70 c# properties syntactic-sugar reference-parameters

我刚刚在C#中工作时遇到此错误消息

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

我知道造成这种情况的原因并快速解决了创建正确类型的局部变量的问题,使用它作为out/ ref参数调用函数,然后将其分配回属性:

RefFn(ref obj.prop);
Run Code Online (Sandbox Code Playgroud)

变成

{
    var t = obj.prop;
    RefFn(ref t);
    obj.prop = t;
}
Run Code Online (Sandbox Code Playgroud)

显然,如果属性不支持在当前上下文中获取和设置,则会失败.

为什么C#不能为我这样做?


我能想到这可能导致问题的唯一情况是:

  • 穿线
  • 例外

对于线程转换影响写入发生时(在函数调用之后与函数调用之间),但我宁愿怀疑任何依赖的代码在它中断时会得到很少的同情.

对于例外,关注的是; 如果函数分配给几个ref参数之一而不是抛出会发生什么?任何一个简单的解决方案都会导致所有参数都被分配给某些参数,而某些参数不应该分配给某些参数.我再次认为这不会支持使用该语言.


注意:我理解为什么会生成此错误消息的机制.我正在寻找的是为什么C#不会自动实现简单的解决方法.

Dav*_*ton 32

因为您传递的是索引器的结果,这实际上是方法调用的结果.不能保证索引器属性也有一个setter,并且当他认为在没有调用setter的情况下设置他的属性时,通过ref传递它将导致开发人员的错误安全性.

在更技术层面上,ref和out传递传递给它们的对象的内存地址,并设置属性,你必须调用setter,所以不能保证该属性实际上会被更改,特别是当属性类型是不可改变的.ref和out不只是在返回方法时设置值,它们将实际的内存引用传递给对象本身.

  • 您描述的问题将解决其自身,因为在编译时,糖将尝试解析setter并失败,并且尝试分配给get only属性会出现同样的错误. (2认同)

use*_*116 17

属性只不过是Java风格的getX/setX方法的语法糖.对方法的'ref'没有多大意义.在你的实例中,它会有意义,因为你的属性只是简化字段.属性不必只是存根,因此框架不允许在属性上'ref'.

编辑:嗯,简单的答案是,一个物业获取者或者设置者可能不仅仅包括一个字段读/写的事实使得它不可取,更不用说可能意外了,允许你提出的那种糖.这并不是说我以前不需要这个功能,只是因为我明白为什么他们不想提供它.


Mar*_*ell 11

仅仅是为了获取信息,C#4.0 将会类似这样的糖,但只有在调用互操作方法时 - 部分原因ref在于这种情况下的纯粹倾向.我没有对它进行过多次测试(在CTP中); 我们必须看看它是如何实现的......


Bri*_*sen 9

您可以使用带ref/的字段out,但不能使用属性.原因是属性实际上只是特殊方法的语法捷径.编译器实际上将get/set属性转换为对应get_Xset_X方法,因为CLR没有对属性的直接支持.


Mar*_*dle 6

它不是线程安全的; 如果两个线程同时创建属性值的自己的副本并将它们作为ref参数传递给函数,则只有其中一个线程最终返回到属性中.

class Program
{
  static int PropertyX { get; set; }

  static void Main()
  {
    PropertyX = 0;

    // Sugared from: 
    // WaitCallback w = (o) => WaitAndIncrement(500, ref PropertyX);
    WaitCallback w = (o) => {
      int x1 = PropertyX;
      WaitAndIncrement(500, ref x1);
      PropertyX = x1;
    };
    // end sugar

    ThreadPool.QueueUserWorkItem(w);

    // Sugared from: 
    // WaitAndIncrement(1000, ref PropertyX);
    int x2 = PropertyX;      
    WaitAndIncrement(1000, ref x2);
    PropertyX = x2;
    // end sugar

    Console.WriteLine(PropertyX);
  }

  static void WaitAndIncrement(int wait, ref int i)
  {
    Thread.Sleep(wait);
    i++;
  }
}
Run Code Online (Sandbox Code Playgroud)

PropertyX最终为1,而字段或局部变量为2.

该代码示例还强调了在要求编译器执行含糖工作时,匿名方法等问题引入的困难.


And*_*are 5

原因是 C# 不支持接受通过引用传递的参数的“参数化”属性。有趣的是,CLR 确实支持此功能,但 C# 不支持。

  • VB.net支持它,我很困惑为什么C#不支持。 (4认同)