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

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

e.g.

Person p = new Person(); 

. . .

public void Test(out p.Name);

person Embedd_0913    schedule 19.02.2009    source источник
comment
Пожалуйста, введите свой вопрос еще раз, чтобы понять смысл ...   -  person Yuval Adam    schedule 19.02.2009
comment
Это имеет достаточно смысла, чтобы получить разумный ответ   -  person Ed Guiness    schedule 19.02.2009
comment
Привет, на самом деле мой вопрос: могу ли я передать свойство как параметр out или ref, если нет, то y нет? пример. Человек p = новый Человек (); public void Test (out p.Name);   -  person Embedd_0913    schedule 19.02.2009


Ответы (3)


Приносим извинения за короткий ответ, но нет, спецификация языка C # запрещает это.

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

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

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

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

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

Это совершенно другое дело для передачи функции, чем адрес переменной.

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

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

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

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

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

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

MyFunc(myObject.IntProperty)

становится

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

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

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

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

person Binary Worrier    schedule 19.02.2009
comment
Сломано. C # не является языком низкого уровня, поэтому параметр ref должен быть не адресом, а фактически парой замыканий, получателем и установщиком. Если вы передаете локальную переменную в качестве параметра ref, язык должен автоматически создать пару геттер / сеттер. И все это нужно скрыть от ничего не подозревающего программиста. - person pyon; 29.06.2011
comment
@ EduardoLeón: Есть много вещей, которые можно сделать с адресами, чего нельзя сделать с помощью замыканий. Правильный способ разрешить передачу свойств в качестве ссылок - это определить вид свойства, которое, вместо того, чтобы содержать геттер и сеттер, предоставляло бы адрес контролируемым образом (например, свойство типа T могло бы иметь метод, который, учитывая делегат, который принял параметр ref типа T, передаст этому делегату адрес резервного поля этого свойства (или другого места хранения типа T). - person supercat; 07.06.2012
comment
Свойство строго эквивалентно паре функций get / set. Вот что есть на самом деле. Поэтому (как указал @ EduardoLeón) все полезное в нем можно захватить с помощью двух закрытий. Я прототипировал что-то подобное в Roslyn: smellegantcode.wordpress.com/2014/04/27/ - person Daniel Earwicker; 18.02.2015
comment
@supercat, хотя более простым усовершенствованием было бы позволить вам использовать ref в автоматических свойствах. Компилятор генерирует их резервное поле, чтобы вместо этого он мог просто заставить ref работать с резервным полем. - person Daniel Earwicker; 18.02.2015
comment
@DanielEarwicker: За исключением доступа, выполняемого внутри класса, который определяет автосвойство, компилятор не будет иметь доступа к полю поддержки. Я не уверен, как ваш подход работает для повышения эффективности; то, что я хотел бы видеть, было бы поддержкой компилятора / фреймворка для свойства ref, так что foo.Location.X+=intVar; переводится во что-то эквивалентное foo.WorkWithBar((ref Point it, ref int dx)=>it.X += dx, ref intVar);. Обратите внимание, что Lambda не закрывается ни над какими переменными, поэтому он должен выполняться быстро, и поскольку it является параметром ref, он может обновляться на месте. - person supercat; 18.02.2015
comment
@ EduardoLeón: И все это нужно скрыть от ничего не подозревающего программиста. В настоящее время гарантируется, что параметр ref не будет иметь побочных эффектов. Например, метод может использовать += для добавления к ref string несколько раз перед возвратом, возможно, проверяя значение по мере его поступления. Программисту метода необходимо знать, поддерживает ли его параметр свойства, поскольку в этом случае им потребуется создать временную переменную. - person hypehuman; 11.12.2015
comment
Кажется, это ограничение out / ref в C # вынудило ValueTuple использовать общедоступные поля вместо автосвойств. - person AkariAkaori; 07.06.2017
comment
@AkariAkaori: Нет, это не имеет ничего общего с решением использовать поля в ValueTuple. Они могли бы реализовать его со свойствами, и он работал бы точно так же, как и сейчас, за исключением того, что вы не сможете передавать значения Tuples как параметры out / ref, что на самом деле не является обязательным требованием. - person Binary Worrier; 07.06.2017
comment
@BinaryWorrier: Какой смысл использовать поля вместо свойств, если бы они не ожидали out / ref в качестве варианта использования? Оптимизатор уже учитывает тривиальные свойства. - person AkariAkaori; 07.06.2017
comment
Пожалуйста, подробно расскажите об очень тонких ошибках, о которых вы говорите. Моя жизнь была бы ТАК ОЧЕНЬ проще прямо сейчас, если бы я мог использовать свойства ref, поэтому мне любопытно, о чем вы говорите. Я думаю, что это должно быть хотя бы разрешено для авто-свойств ... королевский PITA иметь класс, в котором вы просто хотите кучу int Value { get; private set; } свойств, но не можете, потому что вы не можете передать их по ссылке. - person Mike Marynowski; 20.10.2017
comment
Неважно, просто видел пример ниже. Думаю, я понимаю подводные камни, но, черт возьми, это немного раздражает. Записывать все в развернутой форме с резервными полями + свойствами - это гораздо более бессмысленный шаблон, болтовня. - person Mike Marynowski; 20.10.2017

Другие объяснили, что вы не можете сделать это на C #. В 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

Приведенный выше код эквивалентен этому коду 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)
     {
     }
}

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

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

Вот полный пример, в котором передача поля и передача совершенно тривиального свойства в .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

.Net-Fiddle: https://dotnetfiddle.net/ZPFIEZ (разные результаты для поля и свойства)

person Jon Skeet    schedule 19.02.2009
comment
Джон: Если вы не возражаете, не могли бы вы прояснить свои мысли о «мутной воде», я не понимаю, почему вы думаете, что передача свойств в параметры ref - особенно плохая идея (я не говорю, что они хорошая идея). Спасибо друг. - person Binary Worrier; 19.02.2009

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

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);
person Fabian Vilers    schedule 19.02.2009