Могу ли я передать свойство как параметр out или ref, если нет, то почему бы и нет?
e.g.
Person p = new Person();
. . .
public void Test(out p.Name);
Могу ли я передать свойство как параметр out или ref, если нет, то почему бы и нет?
e.g.
Person p = new Person();
. . .
public void Test(out p.Name);
Приносим извинения за короткий ответ, но нет, спецификация языка 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 # должен с этим справиться?
ref должен быть не адресом, а фактически парой замыканий, получателем и установщиком. Если вы передаете локальную переменную в качестве параметра ref, язык должен автоматически создать пару геттер / сеттер. И все это нужно скрыть от ничего не подозревающего программиста.
- person pyon; 29.06.2011
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
ref не будет иметь побочных эффектов. Например, метод может использовать += для добавления к ref string несколько раз перед возвратом, возможно, проверяя значение по мере его поступления. Программисту метода необходимо знать, поддерживает ли его параметр свойства, поскольку в этом случае им потребуется создать временную переменную.
- person hypehuman; 11.12.2015
int Value { get; private set; } свойств, но не можете, потому что вы не можете передать их по ссылке.
- 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 (разные результаты для поля и свойства)
Вместо этого вы должны сделать что-то вроде этого
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);