[英]Is it possible to pass properties as "out" or "ref" parameters?
如果不能,我可以将属性作为“out”或“ref”参数传递,那为什么不呢?
例如
Person p = new Person();
. . .
public void Test(out p.Name);
为简短的回答道歉,但不,C# 语言规范不允许这样做。
请参阅另一个问题的答案,了解尝试时会发生什么。 它还说明了为什么你不应该让房产成为一个公共领域来绕过限制。
希望这可以帮助
编辑:你问为什么?
您将变量传递给out
或ref
参数,您实际上是在传递变量的地址(或内存中的位置)。 在函数内部,编译器知道变量的真正位置,并将值获取和写入该地址。
一个属性看起来像一个值,但它实际上是一对函数,每个函数都有不同的签名。 所以要传递一个属性,你实际上需要传递两个函数指针,一个用于获取,一个用于集合。
传递给函数的东西与传递给变量的地址完全不同
即一个变量地址v 的两个函数指针。
更新
为什么 C# 不为我们处理这个问题?
您正在调用的函数的签名应该是什么?
假设您想调用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# 编译器应该如何处理这个问题?
其他人解释说你不能在 C# 中做到这一点。 在 VB.NET 中,您可以这样做,即使使用选项严格/明确:
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 (字段和属性的不同结果)
不允许这样做的另一个原因是因为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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.