Можно ли передать свойства как параметры "out" или "ref"?

Могу ли я передать свойство как параметр "out" или "ref", если нет, то почему?

например.

Person p = new Person(); 

. , .

public void Test(out p.Name);

Ответ 1

Извините за короткий ответ, но нет, спецификация языка С# запрещает его.

См. этот ответ на другой вопрос, чтобы узнать, что происходит, когда вы пытаетесь. В нем также говорится, почему вы не должны делать свойство просто публичным полем, чтобы обойти ограничение.

Надеюсь, что это поможет

РЕДАКТИРОВАТЬ: спросите, почему?

Вы передаете переменную в параметр out или ref, который вы фактически передаете по адресу (или положению в памяти) этой переменной. Внутри функции компилятор знает, где именно находится переменная, и получает и записывает значения на этот адрес.

Свойство выглядит как значение, но на самом деле это пара функций, каждая из которых имеет другую подпись. Таким образом, чтобы передать свойство, вам действительно нужно передать два указателя на функции, один для get и один для набора.

Это означает, что передать другую функцию, отличную от адреса переменной

то есть. один адрес переменной v - два указателя на функции.

Обновление
Почему С# не заботится об этом для нас?

Я не Эрик Липперт, но я пойду на вопрос, почему

Какая должна быть подпись функции, которую вы вызываете?
Допустим, вы хотите вызвать void MyFn(ref int i), если это останется таким, или если оно изменится, чтобы сказать, что мы также разрешаем свойства? Если он изменится на некоторый синтаксис, например, void MyFn(prop_ref int i), то это бесполезно, вы не можете передавать свойства библиотечным функциям или сторонним кодам, которые не были написаны специальным модификатором prop_ref. В любом случае, я думаю, вы предполагаете, что это не должно быть иначе.

Теперь скажем MyFn передает i в функцию COM или вызов WinAPI, передавая адрес i (т.е. за пределами .net, по ссылке ref). Если это свойство, как вы получаете адрес i? Не может быть никакого фактического int под свойством, чтобы получить адрес. Вы делаете то, что делает VB.Net?

Компилятор Vb.Net указывает, когда свойство передается в качестве аргумента ByRef методу. В этот момент он объявляет переменную, копирует свойство переменной, передает переменную byref, а затем после вызова метода копирует переменную обратно в свойство. то есть.

MyFunc(myObject.IntProperty)

становится

Dim temp_i As Integer = myObject.IntProperty
MyFunc(temp_i)
myObject.IntProperty = temp_i

Любые побочные эффекты свойств происходят не до тех пор, пока MyFunc не вернется, что может вызвать всевозможные проблемы и привести к очень тонким ошибкам.

По моему скромному мнению решение Vb.Net для этой проблемы также нарушено, поэтому я не буду принимать это как ответ.

Как вы думаете, компилятор С# должен справиться с этим?

Ответ 2

Другие объяснили, что вы не можете сделать это на С#. В VB.NET вы можете это сделать, даже с опцией strict/explicit:

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

Приведенный выше код эквивалентен этому С# -коду:

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)
     {
     }
}

Лично я рад, что у С# этого нет - он довольно много загрязняет воды, особенно с точки зрения значения свойства, если параметр задан в методе, но затем генерируется исключение.

РЕДАКТИРОВАТЬ: В соответствии с просьбой, мои рассуждения о том, почему я считаю, что прохождение свойств в мутных водах. Если вы передаете нормальную переменную по ссылке, то эта переменная оценивается каждый раз, когда она ссылается в методе. Если значение по какой-либо причине изменяется (например, как побочный эффект какой-либо другой работы в методе), то это изменение будет немедленно видно в методе. Это не так, если вы передаете свойство по ссылке в 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

Ответ 3

Другая причина, по которой это недопустимо, заключается в том, что параметр ref и out доступен для чтения и записи внутри метода, а свойство может быть только для чтения/записи.

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.

Ответ 4

Вместо этого вы должны сделать что-то вроде этого

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);