Могу ли я передать свойство как параметр "out" или "ref", если нет, то почему?
например.
Person p = new Person();
. , .
public void Test(out p.Name);
Могу ли я передать свойство как параметр "out" или "ref", если нет, то почему?
например.
Person p = new Person();
. , .
public void Test(out p.Name);
Извините за короткий ответ, но нет, спецификация языка С# запрещает его.
См. этот ответ на другой вопрос, чтобы узнать, что происходит, когда вы пытаетесь. В нем также говорится, почему вы не должны делать свойство просто публичным полем, чтобы обойти ограничение.
Надеюсь, что это поможет
РЕДАКТИРОВАТЬ: спросите, почему?
Вы передаете переменную в параметр 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 для этой проблемы также нарушено, поэтому я не буду принимать это как ответ.
Как вы думаете, компилятор С# должен справиться с этим?
Другие объяснили, что вы не можете сделать это на С#. В 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
Другая причина, по которой это недопустимо, заключается в том, что параметр 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.
Вместо этого вы должны сделать что-то вроде этого
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);