是否可以将属性作为“out”或“ref”参数传递?

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

例如

Person p = new Person(); 

。 。 。

 public void Test(out p.Name); 

对于简短的回答道歉,但不是,C#语言规范不允许这样做。

看到另一个问题的答案 ,看看你尝试时会发生什么。 它还说明为什么你不应该让这个属性成为一个公共领域来绕过限制。

希望这可以帮助

编辑:你问为什么?

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

属性看起来像一个值,但它实际上是一对函数,每个函数都有不同的签名。 所以要传递一个属性,你实际上需要传递两个函数指针,一个用于get,另一个用于set。

这是传递给函数而不是变量地址的完全不同的东西

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

更新
为什么C#不为我们照顾这个?

我不是Eric Lippert ,但我会理解为什么

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

现在让我们说MyFni传递给COM函数或WinAPI调用,传递i的地址(即外部.net,通过ref)。 如果是房产,你怎么得到i的地址? 在该属性下可能没有实际的int来获取地址。 你做VB.Net的工作吗?

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

 MyFunc(myObject.IntProperty) 

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

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

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

您认为C#编译器应如何处理?

其他人已经解释过你不能用C#做到这一点。 在VB.NET中,即使使用选项strict / explicit,也可以执行此操作:

 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#没有这个 – 它在水域中非常混乱,特别是在属性值方面,如果参数在方法中设置但随后抛出exception。

编辑:根据要求,我的理由为什么我相信在泥泞的水域传递属性。 如果通过引用传递正常变量,则每次在方法中引用该变量时都会对该变量进行求值。 如果值由于某种原因而发生变化(例如,作为方法中某些其他工作的副作用),那么该变化将立即在方法中可见。 如果您在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 

不允许这样做的另一个原因是因为ref和out参数在方法内是可读写的,而属性可以是readonly / writeonly。

 Person { public string Name { get { return "me"; } } } 

现在怎么办呢?

 Test(out p.Name); public void Test(out string name) { name = "someone else"; } 

你现在不是你,而是其他人,但是这违反了你所做的合同,只get了属性Name (如果有的话)。 这与该类的只读成员字段的情况相同,您无法传递其引用。

 Person { public readonly string name = "me"; } Test(out p.name); //not possible. 

可能是C#可以为方法提供readonly / writeonly参数:

 public void Test(out settable string name, gettable int count, bool whatever) { name = "someone else"; } Test(out p.Name, 0, true); // doesnt compile since p.Name is readonly. 

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

 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);