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

时间:2009-02-09 20:29:56

标签: c# properties syntactic-sugar reference-parameters

我在使用C#

时遇到了此错误消息
  

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

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

RefFn(ref obj.prop);

变成

{
    var t = obj.prop;
    RefFn(ref t);
    obj.prop = t;
}

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

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


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

  • 线程
  • 例外

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

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


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

9 个答案:

答案 0 :(得分:32)

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

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

答案 1 :(得分:17)

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

编辑:嗯,简单的答案是,Property getter或setter可能不仅仅包含字段读/写的事实使得它不可取,更不用说可能出乎意料了,允许你提出的那种糖。这并不是说我以前不需要这个功能,只是因为我理解为什么他们不想提供它。

答案 2 :(得分:11)

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

答案 3 :(得分:9)

您可以使用ref / out但不包含属性的字段。原因是属性实际上只是特殊方法的语法捷径。编译器实际上将get / set属性转换为相应的get_Xset_X方法,因为CLR没有立即支持属性。

答案 4 :(得分: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++;
  }
}

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

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

答案 5 :(得分:4)

当您传递ref / out prepended时,它意味着您正在传递存储在堆中的引用类型。

属性是包装器方法,而不是变量。

答案 6 :(得分:4)

原因是C#不支持接受通过引用传递的参数的“参数”属性。值得注意的是,CLR确实支持这种功能,但C#不支持。

答案 7 :(得分:1)

如果你问为什么编译器不会替换属性getter返回的字段,那是因为getter可以返回一个const或readonly或literal或其他不应该重新初始化或覆盖的东西。 / p>

答案 8 :(得分:0)

此网站似乎有适合您的工作。我没有测试过,所以我不能保证它会起作用。该示例似乎使用反射来访问属性的get和set函数。这可能不是一种推荐的方法,但它可能会满足您的要求。

http://www.codeproject.com/KB/cs/Passing_Properties_byref.aspx