Можно ли передавать свойства как параметры "out" или "ref"? - PullRequest
28 голосов
/ 19 февраля 2009

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

, например

Person p = new Person(); 

. , .

public void Test(out p.Name);

Ответы [ 4 ]

38 голосов
/ 19 февраля 2009

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

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

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

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

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

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

Это совершенно другая вещь, которую нужно передать в функцию, чем адрес переменной

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

Update
Почему 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. В этот момент он объявляет переменную, копирует свойство в переменную, передает переменную byref, а затем после вызова метода копирует переменную обратно в свойство. т.е.

MyFunc(myObject.IntProperty)

становится

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

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

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

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

12 голосов
/ 19 февраля 2009

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

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
2 голосов
/ 11 февраля 2013

Другая причина, по которой это недопустимо, заключается в том, что параметр 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# может предложить аргументы только для чтения / записи для метода:

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 голосов
/ 19 февраля 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);
...