是否可以将属性作为“out”或“ref”参数传递?
Posted
技术标签:
【中文标题】是否可以将属性作为“out”或“ref”参数传递?【英文标题】:Is it possible to pass properties as "out" or "ref" parameters? 【发布时间】:2010-10-08 13:14:09 【问题描述】:如果不能,我可以将属性作为“out”或“ref”参数传递,那为什么不呢?
例如
Person p = new Person();
。 . .
public void Test(out p.Name);
【问题讨论】:
请重新输入您的问题以使其有意义... 它得到了一个明智的答案就足够有意义了 嗨,实际上我的问题是:如果不是,我可以将属性作为“out”或“ref”参数传递吗?例子。人 p = 新人(); public void Test(out p.Name); 【参考方案1】:为简短的回答道歉,但不,C# 语言规范不允许这样做。
看到这个answer 到另一个问题,看看当你尝试时会发生什么。 它还说明了为什么您不应该将属性设置为公共字段以规避限制。
希望对你有帮助
编辑:你问为什么?
您将变量传递给out
或ref
参数实际上是在传递变量的地址(或内存中的位置)。
在函数内部,编译器知道变量的真正位置,并获取并将值写入该地址。
属性看起来像一个值,但它实际上是一对函数,每个函数都有不同的签名。因此,要传递一个属性,您实际上需要传递两个函数指针,一个用于获取,一个用于设置。
传递给函数的东西与传递变量的地址完全不同
即一个变量地址v的两个函数指针。
更新为什么 C# 不为我们解决这个问题?
我不是Eric Lippert,但我会试一试为什么
您要调用的函数的签名应该是什么?
假设您想打电话给void MyFn(ref int i)
,应该保持这种状态,还是应该改为说我们也允许属性?如果它更改为 void MyFn(prop_ref int i)
之类的语法,那么这将毫无用处,您无法将属性传递给库函数或未使用特殊 prop_ref 修饰符编写的第 3 方代码。无论如何,我认为您的建议不应该有所不同。
现在假设MyFn
将i
传递给COM 函数或WinAPI 调用,传递i
的地址(即在.net 外部,通过引用)。如果是房产,如何获得i
的地址?属性下可能没有实际的 int 来获取地址。你做 VB.Net 做的事吗?
当属性作为 ByRef 参数传递给方法时,Vb.Net 编译器会发现。此时它声明了一个变量,将属性复制到变量中,通过引用传递变量,然后在调用方法之后,将变量复制回属性中。即
MyFunc(myObject.IntProperty)
变成
Dim temp_i As Integer = myObject.IntProperty
MyFunc(temp_i)
myObject.IntProperty = temp_i
在MyFunc
返回之前不会发生任何属性副作用,这可能会导致各种问题并导致非常微妙的错误。
在我看来,这个问题的 Vb.Net 解决方案也被破坏了,所以我不会接受它作为答案。
你认为 C# 编译器应该如何处理这个问题?
【讨论】:
这个坏了。 C# 不是低级语言,所以ref
参数不应该是地址,而实际上是一对闭包,一个getter 和一个setter。如果您将局部变量作为ref
参数传递,该语言应自动创建getter/setter 对。而这一切都应该对毫无戒心的程序员隐藏起来。
@EduardoLeón:有很多事情可以用地址完成,而不能用闭包完成。允许将属性作为引用传递的正确方法是定义一种属性,该属性不包含 getter 和 setter,而是以受控方式公开地址(例如,类型 T 的属性可以有一个方法,给定一个接受 T 类型的 ref 参数的委托,会将该属性的支持字段(或 T 类型的其他存储位置)的地址传递给该委托。
一个属性严格等同于一对get/set函数。事实就是这样。因此(正如@EduardoLeón 指出的那样)所有有用的东西都可以被两个闭包捕获。我在 Roslyn 做了这样的原型:smellegantcode.wordpress.com/2014/04/27/…
@supercat 虽然更简单的增强是让您在自动属性上使用 ref。编译器会生成它们的支持字段,因此它可以让 ref 在支持字段上工作。
@EduardoLeón:“所有这些都应该对毫无戒心的程序员隐藏。”目前ref
参数保证没有副作用。例如,一个方法可能会在返回之前多次使用+=
附加到ref string
,可能会随时检查值。该方法的程序员需要知道它的参数是否支持属性,因为在这种情况下,他们需要创建一个临时变量。【参考方案2】:
相反,您应该这样做
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);
【讨论】:
【参考方案3】:其他人已经解释说您不能在 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 被调用一次。这不像您要传递“这是一个 property - 每当您使用参数时都可以从中获取和设置。”
这是一个完整的示例,其中在 .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(字段和属性的结果不同)
【讨论】:
Jon:如果你不介意,你能不能解释一下你对“搅浑水”的想法,我不明白为什么你认为将属性传递给 ref 参数是一个特别糟糕的主意(我'不是说他们是个好主意)。谢谢老兄。以上是关于是否可以将属性作为“out”或“ref”参数传递?的主要内容,如果未能解决你的问题,请参考以下文章