Como otros han señalado, no se puede tener un campo de tipo "árbitro variable". Sin embargo, el solo hecho de saber que no puedes hacerlo es probablemente insatisfactorio; probablemente también quiera saber primero, por qué no, y segundo, cómo evitar esta restricción.
La razón es porque sólo hay tres posibilidades:
1) No permitir campos de tipo ref
2) Permitir campos peligrosos de tipo ref
3) No utilizar el almacenamiento temporal grupo de variables locales (también conocido como "la pila")
Supongamos que permitimos los campos del tipo de referencia. Posteriormente, se podría hacer
public ref int x;
void M()
{
int y = 123;
this.x = ref y;
}
y ahora y se puede acceder después M
ultima. Esto significa que o bien estamos en caso (2): el acceso al this.x
se bloqueará y morirá horriblemente porque el almacenamiento de y ya no existe, o estamos en el caso (3), y el y
local se almacena en la basura montón acumulado, no el grupo de memoria temporal.
Nos gusta la optimización de que las variables locales se almacenen en el grupo temporal incluso si están siendo aprobadas por ref, y odiamos la idea de que pueda dejar una bomba de tiempo que pueda hacer que su programa falle y muera más tarde. Por lo tanto, la opción uno es: sin campos de ref.
Tenga en cuenta que para las variables locales que son variables cerradas de funciones anónimas elegimos la opción (3); esas variables locales no están asignadas fuera del grupo temporal.
Lo que nos lleva a la segunda pregunta: ¿cómo lo solucionamos? Si la razón por la que desea que un campo ref es hacer un getter y setter de otra variable, eso es perfectamente legal:
sealed class Ref<T>
{
private readonly Func<T> getter;
private readonly Action<T> setter;
public Ref(Func<T> getter, Action<T> setter)
{
this.getter = getter;
this.setter = setter;
}
public T Value { get { return getter(); } set { setter(value); } }
}
...
Ref<int> x;
void M()
{
int y = 123;
x = new Ref<int>(()=>y, z=>{y=z;});
x.Value = 456;
Console.WriteLine(y); // 456 -- setting x.Value changes y.
}
y ahí lo tienes. y
se almacena en el montón de gc, y x
es un objeto que tiene la capacidad de obtener y establecer y
.
Tenga en cuenta que el CLR admite los ref locales y los métodos de devolución de ref, aunque C# no. Quizás una versión futura hipotética de C# respalde estas características; Lo prototipé y funciona bien. Sin embargo, esto no es muy importante en la lista de prioridades, así que no me gustaría tener mis esperanzas.
ACTUALIZACIÓN: La característica mencionada en el párrafo anterior finalmente se implementó de manera real en C# 7. Sin embargo, todavía no puede almacenar una referencia en un campo.
¿Te importaría ampliar un poco el segundo párrafo? "un objeto mutable más grande" no está del todo claro para mí. Además, ¿cómo funcionaría un StringWrapper si no hay forma de mantener la referencia de cadena en un campo? –
He dado un ejemplo que debería aclarar. Como dije, en un diseño real, StringWrapper probablemente sea un objeto comercial que contenga algo más que una cadena. –
Gracias por la idea. Mi ejemplo probablemente fue más simplificado porque realmente tengo un objeto (no una cadena) en mi código real que estoy tratando de asignar por referencia. El comportamiento que estoy buscando es permitir que un usuario de un objeto pueda asignar nulo a dicho objeto, o incluso asignar una nueva instancia de ese tipo desde sus métodos. Paso el objeto a través del constructor como parte de una inyección de dependencia y, por lo tanto, los otros métodos solo pueden ver el objeto al acceder a un campo. Parece que lo que quiero no se puede lograr, lo cual es una pena. – Jamie