34

如果不能,我可以将属性作为“out”或“ref”参数传递,那为什么不呢?

例如

Person p = new Person(); 

. . .

public void Test(out p.Name);
4

3 回答 3

43

为简短的回答道歉,但不,C# 语言规范不允许它。

请参阅另一个问题的答案,以了解尝试时会发生什么。它还说明了为什么您不应该将房产设置为公共领域以规避限制。

希望这可以帮助

编辑:你问为什么?

您将一个变量传递给一个outref参数,您实际上是在传递变量的地址(或内存中的位置)。在函数内部,编译器知道变量的真正位置,并获取并将值写入该地址。

属性看起来像一个值,但它实际上是一对函数,每个函数都有不同的签名。因此,要传递一个属性,您实际上需要传递两个函数指针,一个用于获取,一个用于设置。

传递给函数与变量的地址完全不同

即一个变量地址v 的两个函数指针。

更新
为什么 C# 不为我们处理这个?

我不是Eric Lippert,但我会试一试为什么

你调用的函数的签名应该是什么?
假设你想打电话void MyFn(ref int i)应该保持这种状态,还是应该改变说我们也允许属性?如果它更改为类似这样的语法,void MyFn(prop_ref int i)那么这是相当没用的,您不能将属性传递给库函数或未使用特殊 prop_ref 修饰符编写的第 3 方代码。无论如何,我认为您的建议不应该有所不同。

现在让我们说MyFn传递i给 COM 函数或 WinAPI 调用,传递i(即外部 .net,通过 ref)的地址。如果它是一个属性,你如何获得地址i?属性下可能没有实际的 int 来获取地址。你做 VB.Net 做的事吗?

当属性作为 ByRef 参数传递给方法时,Vb.Net 编译器会发现。此时它声明了一个变量,将属性复制到变量中,通过引用传递变量,然后在调用方法之后,将变量复制回属性中。IE

MyFunc(myObject.IntProperty)

变成

Dim temp_i As Integer = myObject.IntProperty
MyFunc(temp_i)
myObject.IntProperty = temp_i

在返回之前不会发生任何属性副作用MyFunc,这可能会导致各种问题并导致非常微妙的错误。

在我看来,这个问题的 Vb.Net 解决方案也被打破了,所以我不会接受它作为答案。

你认为 C# 编译器应该如何处理这个问题?

于 2009-02-19T09:45:28.220 回答
15

其他人解释说您不能在 C# 中执行此操作。在 VB.NET 中,您可以执行此操作,即使使用选项 strict/explicit on:

Option Strict On
Option Explicit On
Imports System.Text

Module Test

   Sub Main()
       Dim sb as new StringBuilder
       Foo (sb.Length)
   End Sub
   
   Sub Foo(ByRef x as Integer)
   End Sub
    
End Module

上面的代码等价于这个 C# 代码:

using System.Text;

class Test
{
     static void Main()
     {
         StringBuilder sb = new StringBuilder();
         int tmp = sb.Length;
         Foo(ref tmp);
         sb.Length = tmp;
     }

     static void Foo(ref int x)
     {
     }
}

就我个人而言,我很高兴 C# 没有这个 - 它使水变得非常混乱,特别是在属性的值方面,如果在方法中设置参数但随后抛出异常。

编辑:根据要求,我关于为什么我相信在水中传递财产的理由。如果您通过引用传递一个普通变量,那么每次在方法中引用它时都会评估该变量。如果值因某种原因发生变化(例如,作为方法中某些其他工作的副作用),则该更改将立即在方法中可见。如果您在 VB.NET 中通过引用传递属性,则情况并非如此:属性 getter 被调用一次,然后属性 setter 被调用一次。这不像您要传入“这是一个属性- 每当您使用参数时都可以从中获取和设置”。

这是一个完整的示例,其中在 .NET 中传递一个字段和传递一个完全无关紧要的属性会产生非常不同的结果:

Option Strict On
Option Explicit On
Imports System.Text

Class Test

   Dim counter as Integer
   
   Property CounterProperty As Integer
       Get
           Return counter
       End Get
       Set (ByVal value as Integer)
           counter = value
       End Set
   End Property
   
   Sub Increment
       counter += 1
   End Sub

   Shared Sub Main()
       Dim t as new Test()
       Console.WriteLine("Counter = {0}", t.counter)
       t.Foo(t.counter)
       Console.WriteLine("Counter = {0}", t.counter)

       t.CounterProperty = 0
       Console.WriteLine("CounterProperty = {0}", t.CounterProperty)
       t.Foo(t.CounterProperty)
       Console.WriteLine("CounterProperty = {0}", t.CounterProperty)
   End Sub
   
   Sub Foo(ByRef x as Integer)
       x = 5
       Increment
       Increment
       Increment
       x += 1
   End Sub
    
End Class

.Net-Fiddle:https ://dotnetfiddle.net/ZPFIEZ (字段和属性的不同结果)

于 2009-02-19T11:09:32.793 回答
0

相反,你应该做这样的事情

WhatEverTheType name;

Test(out name);

// Choose one of the following construction

Person p = new Person();
p.Name = name;

Person p = new Person(name);
Person p = new Person(Name => name);
于 2009-02-19T10:43:08.847 回答