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

发布于 2024-07-13 13:43:49 字数 731 浏览 10 评论 0原文

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

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

我知道导致此问题的原因,并快速解决了创建正确类型的局部变量、将其作为 out/ 调用函数的问题ref 参数,然后将其分配回属性:

RefFn(ref obj.prop);

变成

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

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

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


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

  • 线程
  • 异常

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

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


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

I just ran across this error message while working in C#

A property or indexer may not be passed as an out or ref parameter

I known what caused this and did the quick solution of creating a local variable of the correct type, calling the function with it as the out/ref parameter and then assigning it back to the property:

RefFn(ref obj.prop);

turns into

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

Clearly this would fail if the property doesn't support get and set in the current context.

Why doesn't C# just do that for me?


The only cases where I can think of where this might cause problems are:

  • threading
  • exceptions

For threading that transformation affects when the writes happen (after the function call vs. in the function call), but I rather suspect any code that counts on that would get little sympathy when it breaks.

For exceptions, the concern would be; what happens if the function assigns to one of several ref parameters than throws? Any trivial solution would result in all or none of the parameters being assigned to when some should be and some should not be. Again I don't think this would be supported use of the language.


Note: I understand the mechanics of why this error messages is generated. What I'm looking for is the rationale for why C# doesn't automatically implement the trivial workaround.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

雨落星ぅ辰 2024-07-20 13:43:49

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

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

Because you're passing the result of the indexer, which is really the result of a method call. There's no guarantee that the indexer property also has a setter, and passing it by ref would lead to a false security on the developer's part when he thinks that his property is going to be set without the setter being called.

On a more technical level, ref and out pass the memory address of the object passed into them, and to set a property, you have to call the setter, so there's no guarantee that the property would actually be changed especially when the property type is immutable. ref and out don't just set the value upon return of the method, they pass the actual memory reference to the object itself.

荒岛晴空 2024-07-20 13:43:49

属性只不过是 Java 风格的 getX/setX 方法的语法糖。 对于方法上的“ref”来说没有多大意义。 在您的实例中,这是有意义的,因为您的属性只是删除字段。 属性不必只是存根,因此框架不能允许在属性上使用“ref”。

编辑:好吧,简单的答案是,属性 getter 或 setter 可能包含的不仅仅是字段读/写,这一事实使得允许排序是不受欢迎的,更不用说可能是意外的了你建议的糖。 这并不是说我以前不需要这个功能,只是我理解为什么他们不想提供它。

Properties are nothing more than syntactic sugar over the Java style getX/setX methods. It doesn't make much sense for 'ref' on a method. In your instance it would make sense because your properties are merely stubbing out fields. Properties don't have to just be stubs, hence the framework cannot allow 'ref' on Properties.

EDIT: Well, the simple answer is that the mere fact that a Property getter or setter could include far more than just a field read/write makes it undesirable, not to mention possibly unexpected, to allow the sort of sugar you are proposing. This isn't to say I haven't been in need of this functionality before, just that I understand why they wouldn't want to provide it.

故事↓在人 2024-07-20 13:43:49

仅供参考,C# 4.0 将会有一些这样的糖,但仅在调用互操作方法时 - 部分原因是 ref 的纯粹倾向这个场景。 我还没有对其进行太多测试(在CTP中); 我们得看看结果如何……

Just for info, C# 4.0 will have something like this sugar, but only when calling interop methods - partly due to the sheer propensity of ref in this scenario. I haven't tested it much (in the CTP); we'll have to see how it pans out...

野味少女 2024-07-20 13:43:49

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

You can use fields with ref/out, but not properties. The reason is that properties are really just a syntax short cut for special methods. The compiler actually translates get / set properties to corresponding get_X and set_X methods as the CLR has no immediate support for properties.

是伱的 2024-07-20 13:43:49

它不是线程安全的; 如果两个线程同时创建自己的属性值副本并将它们作为 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。

该代码示例还强调了在要求编译器执行甜蜜操作时,匿名方法等内容带来的困难。

It wouldn't be thread-safe; if two threads simultaneously create their own copies of the property value and pass them to functions as ref parameters, only one of them ends up back in the property.

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 ends up as 1, whereas a field or local variable would be 2.

That code sample also highlights the difficulties introduced by things like anonymous methods when asking the compiler to do sugary stuff.

剑心龙吟 2024-07-20 13:43:49

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

The reason for this is that C# does not support "parameterful" properties that accept parameters passed by reference. It is interesting to note that the CLR does support this functionalty but C# does not.

怕倦 2024-07-20 13:43:49

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

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

When you pass ref/out prepended it means that you are passing a reference type which is stored in the heap.

Properties are wrapper methods, not variables.

晚雾 2024-07-20 13:43:49

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

If you're asking why the compiler doesn't substitute the field returned by the property's getter, it's because the getter can return a const or readonly or literal or something else that shouldn't be re-initialized or overwritten.

寒冷纷飞旳雪 2024-07-20 13:43:49

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

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

This site appears to have a work around for you. I have not tested it though, so I can't guarantee it will work. The example appears to use reflection in order to gain access to the get and set functions of the property. This is probably not a recommended approach, but it might accomplish what you're asking for.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文