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

我只是在使用 C # 时遇到了这个错误消息

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

我知道是什么原因导致了这种情况,所以我快速地创建了一个类型正确的局部变量,用它作为 out/ref参数调用函数,然后将它返回给属性:

RefFn(ref obj.prop);

变成了

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

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

为什么 C # 不能帮我做到这一点呢?


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

  • 穿线
  • 例外

因为线程化转换会影响写操作的发生时间(在函数调用之后还是在函数调用中) ,但是我相信任何依赖于这种转换的代码在中断时都不会得到什么同情。

对于异常,需要考虑的问题是: 如果函数分配给几个 ref参数中的一个,而不是抛出,会发生什么?任何琐碎的解决方案都会导致在应该和不应该分配参数的情况下分配所有或全部参数。同样,我认为这种语言的使用不会得到支持。


注意: 我了解生成此错误消息的机制。我所寻找的是为什么 C # 不自动实现这个简单的解决方案的理由。

38288 次浏览

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

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

在更技术的层面上,ref 和 out 传递传递给它们的对象的内存地址,要设置一个属性,你必须调用 setter,所以不能保证属性实际上会被更改,特别是当属性类型是不可变的时候。Ref 和 out 不仅仅是方法返回时的值 准备好了,它们还将实际的内存引用传递给对象本身。

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

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

属性只不过是 Java 风格的 getX/setX 方法的语法糖。对于方法的“ ref”没有多大意义。在您的实例中,这样做是有意义的,因为您的属性只是将字段存根化。属性不必只是存根,因此框架不允许对属性进行“ ref”。

编辑 : 好吧,简单的答案是,单纯的事实,一个属性获取器或设置器可以包含远远超过只是一个字段读/写使它不受欢迎,更不用说可能出乎意料,允许你提议的那种糖。这并不是说我以前不需要这个功能,只是我理解他们为什么不想提供这个功能。

仅供参考,C # 4.0 威尔有一些 喜欢这样的功能,但只有在调用互操作方法时才有,部分原因是在这个场景中 ref的倾向性。我还没有测试它太多(在 CTP) ; 我们将看到它如何结束..。

如果你问为什么编译器不替换属性 getter 返回的字段,那是因为 getter 可以返回 const、 readonly、 Literal 或其他不应该被重新初始化或覆盖的内容。

这个网站似乎为您提供了一个工作环境。我还没有测试过,所以我不能保证它会工作。该示例似乎使用反射来获取对属性的 get 和 set 函数的访问权。这可能不是一个推荐的方法,但它可能会实现您的要求。

Http://www.codeproject.com/kb/cs/passing_properties_byref.aspx

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

它不是线程安全的; 如果两个线程同时创建属性值的副本并将它们作为参数传递给函数,那么只有其中一个会返回到属性中。

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。

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