C# 是否可以将属性作为“out”或“ref”参数传递?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/564557/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it possible to pass properties as "out" or "ref" parameters?
提问by Embedd_Khurja
Can I pass a property as an "out" or "ref" parameter if not then why not?
如果不能,我可以将属性作为“out”或“ref”参数传递,那为什么不呢?
e.g.
例如
Person p = new Person();
. . .
. . .
public void Test(out p.Name);
采纳答案by Binary Worrier
Apologies for the short answer, but no, the C# language specification disallows it.
为简短的回答道歉,但不,C# 语言规范不允许这样做。
See this answerto another question to see what happens when you try. It also says why you shouldn't make the property just be a public field to get around the restriction.
请参阅另一个问题的答案,了解尝试时会发生什么。它还说明了为什么你不应该让房产成为一个公共领域来绕过限制。
Hope this helps
希望这可以帮助
EDIT: You ask Why?
编辑:你问为什么?
You pass a variable to an out
or ref
parameter you're actually passing the address (or location in memory) of the variable.
Inside the function the compiler knows where the variable really is, and gets and writes values to that address.
您将变量传递给实际传递变量的地址(或内存中的位置)的out
orref
参数。在函数内部,编译器知道变量的真正位置,并将值获取和写入该地址。
A property looks like a value, buts it's actually a pair of functions, each with a different signature. So to pass a property, you'd actually need to pass two function pointers, one for the get, and one for the set.
一个属性看起来像一个值,但它实际上是一对函数,每个函数都有不同的签名。所以要传递一个属性,你实际上需要传递两个函数指针,一个用于获取,一个用于集合。
Thats a completely different thing to pass to a function than the address of a variable
传递给函数的东西与传递给变量的地址完全不同
i.e. one variable address v's two function pointers.
即一个变量地址v 的两个函数指针。
Update
Why doesn't C# just look after this for us?
更新
为什么 C# 不为我们处理这个问题?
I'm no Eric Lippert, but I'll have a go at why
What should the signature of the function you're calling be?
Lets say you want to call void MyFn(ref int i)
should that remain that way, or should it change to say we also allow properties? If it changes to some syntax like void MyFn(prop_ref int i)
then this is fairly useless, you can't pass properties to library functions or 3rd party code that wasn't written with the special prop_ref modifier. Anyway I think you're suggesting it shouldn't be different.
您正在调用的函数的签名应该是什么?
假设您想调用void MyFn(ref int i)
应该保持这种方式,还是应该更改为我们也允许属性?如果它更改为某些语法,void MyFn(prop_ref int i)
那么这是相当无用的,您无法将属性传递给库函数或未使用特殊 prop_ref 修饰符编写的第 3 方代码。无论如何,我认为你建议它不应该有所不同。
Now lets say MyFn
passes i
to a COM function, or WinAPI call, passing the address of i
(i.e. outside .net, by ref). If it's a property, how do you get the address of i
? There may be no actual int under the property to get the address of. Do you do what VB.Net does?
现在让我们说MyFn
传递i
给 COM 函数或 WinAPI 调用,传递地址i
(即在 .net 之外,通过 ref)。如果是房产,你如何获得的地址i
?属性下可能没有实际的 int 来获取其地址。你做 VB.Net 做的事吗?
The Vb.Net compiler spots when a property is passed as a ByRef argument to a method. At that point it declares a variable, copies the property to the variable, passes the variable byref and then after the method is called, copies the variable back into the property. i.e.
当属性作为 ByRef 参数传递给方法时,Vb.Net 编译器会发现。此时它声明一个变量,将属性复制到变量,通过引用传递变量,然后在调用方法后,将变量复制回属性。IE
MyFunc(myObject.IntProperty)
becomes
变成
Dim temp_i As Integer = myObject.IntProperty
MyFunc(temp_i)
myObject.IntProperty = temp_i
Any property side effects don't happen until MyFunc
returns, which can cause all sorts of problems and lead to verysubtle bugs.
在MyFunc
返回之前不会发生任何属性副作用,这可能会导致各种问题并导致非常微妙的错误。
In my humble opinion the Vb.Net solution to this problem is also broken, so I'm not going to accept that as an answer.
在我看来,这个问题的 Vb.Net 解决方案也被破坏了,所以我不会接受它作为答案。
How do you think the C# compiler should handle this?
你认为 C# 编译器应该如何处理这个问题?
回答by Fabian Vilers
Instead, you should do something like this
相反,你应该做这样的事情
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);
回答by Jon Skeet
Others have explained that you can't do this in C#. In VB.NET, you cando this, even with option strict/explicit on:
其他人解释说你不能在 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
The above code is equivalent to this C# code:
上面的代码等价于这个 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)
{
}
}
Personally I'm glad that C# doesn't have this - it's muddying the waters quite a lot, particularly in terms of the value of the property if the parameter is set within the method but then an exception is thrown.
就我个人而言,我很高兴 C# 没有这个 - 它把水搅浑了很多,特别是如果在方法中设置了参数但随后抛出了异常,那么就属性的值而言。
EDIT: As requested, my reasoning as to why I believe passing properties in muddies the waters. If you pass a normal variable by reference, then that variable is evaluated every time it is referenced within the method. If the value changes for some reason (e.g. as a side-effect of some other work in the method) then that change will be immediately visible in the method. That's not the case if you pass a property by reference in VB.NET: the property getter is invoked once, and then the property setter is invoked once. It's not like you're passing in "here's a property- get and set from that whenever you use the parameter."
编辑:根据要求,我对为什么我相信在泥泞中传递财产的推理。如果通过引用传递普通变量,则每次在方法中引用该变量时都会评估该变量。如果由于某种原因值发生变化(例如作为方法中其他工作的副作用),那么该变化将在方法中立即可见。如果您在 VB.NET 中通过引用传递属性,则情况并非如此:属性 getter 被调用一次,然后属性 setter 被调用一次。这不像您传入“这是一个属性- 每当您使用参数时从中获取和设置”。
Here's a full example where passing a field and passing an entirely trivial property in .NET have very different results:
这是一个完整的示例,在 .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
回答by nawfal
Another reason this isn't permitted is because the ref and out parameter is readable and writable inside a method, while a property can be readonly/writeonly.
不允许这样做的另一个原因是 ref 和 out 参数在方法内部是可读和可写的,而属性可以是只读/只写。
Person
{
public string Name { get { return "me"; } }
}
Now what if you can do this?
现在如果你能做到这一点呢?
Test(out p.Name);
public void Test(out string name)
{
name = "someone else";
}
You are now not you, but someone else but that's against the contract you made with get
only property Name
(if ever this worked). This is the same case with readonly member fields of the class, you cant pass their reference.
你现在不是你,而是其他人,但这违背了你get
只用财产订立的合同Name
(如果这有效的话)。这与类的只读成员字段的情况相同,您不能传递它们的引用。
Person
{
public readonly string name = "me";
}
Test(out p.name); //not possible.
May be C#
can come up with a readonly/writeonly arguments for a method:
可能C#
可以为方法提供只读/只写参数:
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.