c# - method - ¿Es posible pasar propiedades como parámetros de "salida" o "ref"?
c# variables by reference (4)
En cambio, deberías hacer algo como esto
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);
¿Puedo pasar una propiedad como un parámetro de "salida" o "ref"? Si no, ¿por qué no?
p.ej
Person p = new Person();
. . .
public void Test(out p.Name);
Otros han explicado que no se puede hacer esto en C #. En VB.NET, puede hacer esto, incluso con la opción estricta / explícita en:
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
El código anterior es equivalente a este código 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)
{
}
}
Personalmente, me alegra que C # no tenga esto: está enturbiando mucho las aguas, particularmente en términos del valor de la propiedad si el parámetro se establece dentro del método, pero luego se lanza una excepción.
EDITAR: Según lo solicitado, mi razonamiento sobre por qué creo que pasar propiedades en los fangos de las aguas. Si pasa una variable normal por referencia, entonces esa variable se evalúa cada vez que se hace referencia dentro del método. Si el valor cambia por alguna razón (por ejemplo, como un efecto secundario de algún otro trabajo en el método), ese cambio será inmediatamente visible en el método. Ese no es el caso si pasas una propiedad por referencia en VB.NET: el getter de la propiedad se invoca una vez, y luego se invoca el setter de la propiedad una vez. No es como si estuvieras pasando "aquí hay una propiedad : obtén y configúrala cada vez que uses el parámetro".
Aquí hay un ejemplo completo donde pasar un campo y pasar una propiedad totalmente trivial en .NET tiene resultados muy diferentes:
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
Otra razón por la que esto no está permitido es porque el parámetro ref y out es legible y escribible dentro de un método, mientras que una propiedad puede ser de solo lectura / escritura.
Person
{
public string Name { get { return "me"; } }
}
¿Y ahora si puedes hacer esto?
Test(out p.Name);
public void Test(out string name)
{
name = "someone else";
}
Ahora no eres tú, sino otra persona, pero eso va en contra del contrato que hiciste con get
solo el Name
propiedad (si alguna vez funcionó). Este es el mismo caso con los campos de solo lectura de la clase, no puedes pasar su referencia.
Person
{
public readonly string name = "me";
}
Test(out p.name); //not possible.
Puede ser C#
puede venir con un argumento readonly / writeonly para un método:
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.
Disculpas por la respuesta corta, pero no, la especificación del lenguaje C # no lo permite.
Vea esta respuesta a otra pregunta para ver qué sucede cuando lo intenta. También dice por qué no debe hacer que la propiedad sea solo un campo público para evitar la restricción.
Espero que esto ayude
EDIT: preguntas por qué?
Si pasa una variable a un parámetro de out
o ref
, en realidad está pasando la dirección (o ubicación en la memoria) de la variable. Dentro de la función, el compilador sabe dónde está realmente la variable y obtiene y escribe valores en esa dirección.
Una propiedad se ve como un valor, pero en realidad es un par de funciones, cada una con una firma diferente. Por lo tanto, para pasar una propiedad, en realidad necesitaría pasar dos punteros a función, uno para obtener y otro para el conjunto.
Eso es algo completamente diferente para pasar a una función que la dirección de una variable
es decir, una dirección variable v dos punteros de función.
Actualizar
¿Por qué C # no se ocupa de esto por nosotros?
No soy Eric Lippert , pero voy a ver por qué
¿Cuál debería ser la firma de la función a la que llamas?
Digamos que quieres llamar a void MyFn(ref int i)
si eso sigue así, ¿o debería cambiar para decir que también permitimos propiedades? Si cambia a alguna sintaxis como void MyFn(prop_ref int i)
entonces esto es bastante inútil, no puede pasar propiedades a las funciones de la biblioteca o código de terceros que no fue escrito con el modificador especial prop_ref. De todos modos, creo que estás sugiriendo que no debería ser diferente.
Ahora digamos que MyFn
i
pasa a una función COM, o llamada a WinAPI, pasando la dirección de i
(es decir, fuera de .net, por ref). Si es una propiedad, ¿cómo se obtiene la dirección de i
? No puede haber ninguna int real debajo de la propiedad para obtener la dirección de. ¿Haces lo que hace VB.Net?
El compilador Vb.Net detecta cuando una propiedad se pasa como un argumento ByRef a un método. En ese momento, declara una variable, copia la propiedad en la variable, pasa la variable byref y luego, una vez que se llama al método, copia la variable nuevamente en la propiedad. es decir
MyFunc(myObject.IntProperty)
se convierte
Dim temp_i As Integer = myObject.IntProperty
MyFunc(temp_i)
myObject.IntProperty = temp_i
Cualquier efecto secundario de propiedad no ocurre hasta que MyFunc
regrese, lo que puede causar todo tipo de problemas y provocar errores muy sutiles.
En mi humilde opinión, la solución Vb.Net para este problema también está rota, así que no voy a aceptar eso como una respuesta.
¿Cómo crees que el compilador de C # debería manejar esto?