Is it possible to pass properties as “out” or “ref” parameters?

前端 未结 4 1545
猫巷女王i
猫巷女王i 2020-11-28 14:01

Can I pass a property as an \"out\" or \"ref\" parameter if not then why not?

e.g.

Person p = new Person(); 

. . .



        
相关标签:
4条回答
  • 2020-11-28 14:40

    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);
    
    0 讨论(0)
  • 2020-11-28 14:49

    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?

    0 讨论(0)
  • 2020-11-28 14:51

    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
    
    0 讨论(0)
  • 2020-11-28 14:56

    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.

    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.

    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:

    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.
    
    0 讨论(0)
提交回复
热议问题