9

C#: can 'out' parameters in functions be object properties/variables?

eg:

can I call a function as follows:

someFunction(x, y, out myObject.MyProperty1)
CJ7
  • 22,579
  • 65
  • 193
  • 321

5 Answers5

6

Properties are [a pair of get and set] methods with a cute syntax for invoking them that makes them look like fields.

Out parameters can be references to fields or stack based 'fields' (i.e., locals).

The bridging of this gap is generally not addressed by languages (and definitely not directly by the CLR and IL model). (+1 on @Heinzi for the VB trickery references)

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • 1
    A rationale for the -1 would be appreciated (other than the fact that @Mark Byers' answer has grown to incorporate this angle since its initial posting?) – Ruben Bartelink Aug 06 '10 at 14:58
  • 1
    @Craig Johnston: I commend you on seeing through the fog of the answers and debate your seemingly straightforward question provoked! – Ruben Bartelink Aug 13 '10 at 07:30
  • +1: To to this properly you would indeed need to capture a pair of closures for each property. At that point `ref int i` will hide some fairly involved code . . . but only when a property is passed. Big difference between de-referencing an int and carting around & using pairs of closures. P.s. I can't stand downvotes without comments. – Binary Worrier Aug 13 '10 at 12:45
  • @Binary Worrier: Bottom line is the CLR directly supports references at the IL level, and `out` maps down to `&` - which means there's no way to achieve this without the language transforming all usages of ref and int to the pair of delegates of which you speak. (http://www.inishtech.com/FAQ24 is one reason I happen to have thought through this jazz!). I suspect (having not read @Heinzi's citation) that this VB.NET madness probably only works on privates (even internals would be a lot to ask). BTW, why do I always bump into you on fight club threads?! – Ruben Bartelink Aug 13 '10 at 13:53
  • 1
    Here here! You can do this with any property in VB.Net, regardless of scope. Basically every call with a property to a `ByRef` arg copies the property value to a local, performs the call, then update the property with the local e.g. `AMethod(myObj.MyProperty)` becomes `var temp = myObj.MyProperty; AMethod(ref temp); myObj.MyProperty = temp`. Which is plain fcukin dumb imho. I added an answer showing why. P.S. Because these are the interesting threads. – Binary Worrier Aug 13 '10 at 14:47
4

No, you cannot use a property as a ref or out parameter in C#, since the CLR does not support this. Passing an instance variable should work fine.

As a side note, VB.NET allows passing properties and uses a technique called "copy back ByRef", as explained in this MSDN blog entry.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • 2
    +1: I strongly advise against passing properties byref in VB.Net, can have surprising side effects, better if - like C# - it was unsupported. – Binary Worrier Aug 06 '10 at 08:36
  • 2
    @Binary: do you have any evidence/references for this claim? – CJ7 Aug 06 '10 at 08:41
  • @Craig: Have added my own "answer" below, with VB code samples that shows what I mean http://stackoverflow.com/questions/3422078/c-can-out-parameters-in-functions-be-object-properties-variables/3424123#3424123 – Binary Worrier Aug 06 '10 at 13:23
4

You can't do this with C#.
You can with VB.Net, but I consider this a bad idea. The following code and output show how it does it and shows why I think it's a bad idea, to the point where I wish VB.net also didn't allow this

Public Class MySimpleClass
    Private _privateInt As Integer
    Public PublicInt As Integer

    Public Property PrivateInt() As Integer
        Get
            Return _privateInt
        End Get
        Set(ByVal value As Integer)
            _privateInt = value
        End Set
    End Property

    Public Sub MyNotifier()
        Console.WriteLine("PublicInt {0} : PrivateInt {1} : Values are not the same", PublicInt, PrivateInt)
    End Sub

End Class

Now call this from a sub main, like so

Sub Main()
    Dim sampleClass As New MySimpleClass
    IterateAndUpdate(sampleClass.PrivateInt, sampleClass.PublicInt, AddressOf sampleClass.MyNotifier)

    Console.WriteLine("Private {0} : Public {0} : values are the same ", sampleClass.PrivateInt, sampleClass.PublicInt)
    Console.ReadKey()
End Sub

Sub IterateAndUpdate(ByRef anInt As Integer, ByRef anOtherInt As Integer, ByVal notifier As Action)
    For i As Integer = 1 To 9
        anInt = i
        anOtherInt = i
        notifier()
    Next
End Sub

This outputs

PublicInt 1 : PrivateInt 0 : Values are not the same
PublicInt 2 : PrivateInt 0 : Values are not the same
PublicInt 3 : PrivateInt 0 : Values are not the same
PublicInt 4 : PrivateInt 0 : Values are not the same
PublicInt 5 : PrivateInt 0 : Values are not the same
PublicInt 6 : PrivateInt 0 : Values are not the same
PublicInt 7 : PrivateInt 0 : Values are not the same
PublicInt 8 : PrivateInt 0 : Values are not the same
PublicInt 9 : PrivateInt 0 : Values are not the same
Private 9 : Public 9 : values are the same

You can see that the PublicInt member when passed ByRef is updated immediately on sampleClass, put the PrivateInt exposed as a property is only updated after the IterateAndUpdate method ends.

Therefore, you get markedly different behaviour from exactly the same calling convention, and it depends on how you've implemented the item passed (which isn't at all obvious looking at the call to IterateAndUpdate.
The potential to hide bugs, or have small changes change expected behaviour is enough for me to wish this "feature" didn't exist.

To my mind this does not work correctly, therefore it should either be
a) Fixed, which would take considerable effort for the compiler team, and possibly introduce breaking changes
or
b) Not work at all

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Binary Worrier
  • 50,774
  • 20
  • 136
  • 184
3

The argument passed to an out parameter cannot be a property. If you try to compile your code you will get a compile error.

A property, indexer or dynamic member access may not be passed as an out or ref parameter

The reason is that a property is not actually field (it might have a backing field, but it might not). It is two methods called get_Foo and set_Foo. See c# property and ref parameter, why no sugar? for more explanation of why this doesn't work.

Example code that gives the compile error:

class Program
{
    public int Foo { get; set; }
    public void Bar(out int x)
    {
        x = 5;
    }
    void Run()
    {
        Bar(out Foo); // compile error 
    }
    static void Main()
    {
        new Program().Run();
    }
}
Community
  • 1
  • 1
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
0

You can use the out parameter with fields (or locals as said already).

leppie
  • 115,091
  • 17
  • 196
  • 297