Is it possible to pass properties as "out" or "ref" parameters?
Solution 1
Apologies for the short answer, but no, the C# language specification disallows it.
See this answer to 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.
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.
Update
Why doesn't C# just look after this for us?
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.
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?
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.
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 very subtle bugs.
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.
How do you think the C# compiler should handle this?
Solution 2
Others have explained that you can't do this in C#. In VB.NET, you can do this, even with option 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
The above code is equivalent to this C# code:
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.
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."
Here's a full example where passing a field and passing an entirely trivial property in .NET have very different results:
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 (different results for field and property)
Embedd_0913
Updated on May 25, 2021Comments
-
Embedd_0913 about 3 years
Can I pass a property as an "out" or "ref" parameter if not then why not?
e.g.
Person p = new Person();
. . .
public void Test(out p.Name);
-
Rabeel over 15 yearsJon: If you don't mind, could you elucidate your thoughts on “muddying the waters”, I'm not getting why you think passing properties into ref parameters are is a particularly bad idea (I'm not saying they're a good idea). Thanks mate.
-
pyon about 13 yearsThis is broken. C# is not a low level language, so a
ref
parameter should not be an address, but actually a pair of closures, a getter and a setter. If you pass a local variable as aref
parameter, the language should automatically create a getter/setter pair. And all this should be hidden from the unsuspecting programmer. -
supercat about 12 years@EduardoLeón: There are many things which can be done with addresses which cannot be done with closures. The proper way to allow properties to be passed as references would be to define a kind of property which, rather than containing a getter and setter, would expose an address in controlled fashion (e.g. a property of type T could have a method which, given a delegate that accepted a ref parameter of type T, would pass the address of that property's backing field (or other storage location of type T) to that delegate.
-
Daniel Earwicker over 9 yearsA property is strictly equivalent to a get/set pair of functions. That's what it really is. Therefore (as @EduardoLeón pointed out) everything useful about it can be captured by two closures. I have prototyped something like this in Roslyn: smellegantcode.wordpress.com/2014/04/27/…
-
Daniel Earwicker over 9 years@supercat although a simpler enhancement would be to let you use ref on auto-properties. The compiler generates their backing field so it can just make ref work on the backing field instead.
-
supercat over 9 years@DanielEarwicker: Except for accesses performed within the class that defines the autoproperty, a compiler won't have access to the backing field. I'm not sure how your approach works for efficiency; what I would have liked to have seen would have been compiler/framework support for a
ref
property, such thatfoo.Location.X+=intVar;
would translate into something equivalent tofoo.WorkWithBar((ref Point it, ref int dx)=>it.X += dx, ref intVar);
Note that the Lambda doesn't close over any variables, so it should execute quickly, and becauseit
is aref
param, it can update in-place. -
hypehuman over 8 years@EduardoLeón: "And all this should be hidden from the unsuspecting programmer." Currently a
ref
parameter is guaranteed to have no side effects. For example, a method might use+=
to append to aref string
multiple times before returning, possibly checking the value as it goes. The programmer of the method would need to know if its parameter supports properties, since in that case, they would need to create a temporary variable instead. -
AkariAkaori about 7 yearsSeems this limitation of out/ref in C# has forced the ValueTuple to use public fields instead of autoproperties.
-
Rabeel about 7 years@AkariAkaori: No, this has nothing to do with the decision to use fields on the ValueTuple. They could have implemented it with properties, and it would work exactly as it currently does, except you wouldn't be able to pass the Tuples values as out/ref parameters, which isn't actually a requirement.
-
AkariAkaori about 7 years@BinaryWorrier: What would be the point of using fields instead of properties if they weren't expecting out/ref as a use case? The optimizer already accounts for trivial properties.
-
Mike Marynowski over 6 yearsPlease elaborate on the very subtle bugs you speak of. My life would be SOOO much easier right now if I could use ref properties so I'm curious what you're talking about. I think it should at least be allowed for auto-properties...it's a royal PITA to have a class you just want a bunch of
int Value { get; private set; }
properties on but can't because you can't pass them by ref. -
Mike Marynowski over 6 yearsNevermind, just saw the example below. I guess I understand the pitfalls but damn it's a bit annoying. Writing out everything long-form with backing fields + properties is so much more pointless boilerplate, blarg.