2011-10-21 11 views
7

Me gustaría asignar una referencia a un campo de miembro. Pero, obviamente, no entiendo esta parte de C# muy bien, porque no pude :-) Por lo tanto, aquí está mi código:Establecer una referencia a un campo de miembro en C#

public class End { 
    public string parameter; 

    public End(ref string parameter) { 
     this.parameter = parameter; 
     this.Init(); 
     Console.WriteLine("Inside: {0}", parameter); 
    } 

    public void Init() { 
     this.parameter = "success"; 
    } 
} 

class MainClass { 
    public static void Main(string[] args) { 
      string s = "failed"; 
     End e = new End(ref s); 
     Console.WriteLine("After: {0}", s); 
    } 
} 

de salida es:

Inside: failed 
After: failed 

¿Cómo llego "éxito" en la ¿la consola?

Gracias de antemano, dijxtra

+0

Por cierto, es lógico que obtenga el mismo resultado de salida (Sin embargo, entiendo que está esperando que se imprima el resultado 'success') porque ambas líneas' Console.WriteLine' se ejecutan * después * de 'Init () 'método. – Otiel

Respuesta

3

En realidad, hay dos problemas aquí.

Uno, como han dicho los otros carteles, no puedes estrictamente hacer lo que estás buscando hacer (ya que puedes con C y similares). Sin embargo, el comportamiento y la intención todavía son realizables en C#, solo tienes que hacerlo de la manera C#.

El otro problema es su desafortunado intento de probar y usar cadenas, que son, como uno de los otros carteles mencionados, inmutables, y por definición se copian.

Así, una vez dicho esto, el código se puede convertir fácilmente a esto, que creo que no hace lo que quiere:

public class End 
{ 
    public StringBuilder parameter; 

    public End(StringBuilder parameter) 
    { 
     this.parameter = parameter; 
     this.Init(); 
     Console.WriteLine("Inside: {0}", parameter); 
    } 

    public void Init() 
    { 
     this.parameter.Clear(); 
     this.parameter.Append("success"); 
    } 
} 

class MainClass 
{ 
    public static void Main(string[] args) 
    { 
     StringBuilder s = new StringBuilder("failed"); 
     End e = new End(s); 
     Console.WriteLine("After: {0}", s); 
    } 
} 
+0

Esta solución es la más elegante de las propuestas aquí, gracias :-) – dijxtra

+0

Preguntas y respuestas informativas, pero tengo que estar en desacuerdo, usar 'StringBuilder' de esta manera rompe su propósito semántico y es posiblemente más costoso. Es posiblemente el camino más corto, LOC-wise, pero va "contra la corriente" de la CLR. El simple 'MyRef ' de Eric comunica el propósito más limpiamente y tiene el beneficio adicional de trabajar para cualquier tipo dado. Muy agradable. –

+0

@MarcL. - Sí, estoy de acuerdo con eso. Mi enfoque fue definitivamente rápido y sucio, basado principalmente en lo que percibí como la reacción (negativa) a la pregunta cuando se publicó inicialmente. – Steve

2

Suena como lo que estamos tratando de hacer aquí es hacer un campo de una referencia a otra ubicación de almacenamiento. Esencialmente tiene un campo ref de la misma manera que tiene un parámetro ref. Esto no es posible en C#.

Uno de los principales problemas al hacer esto es que para ser verificable, el CLR (y C#) deben poder demostrar que el objeto que contiene el campo no durará más que la ubicación que apunta. Esto es típicamente imposible ya que los objetos viven en el montón y un ref puede apuntar fácilmente a la pila. Estos dos lugares tienen una semántica de vida muy diferente (por lo general, el montón es más largo que la pila) y, por lo tanto, no se puede demostrar que un ref entre válido.

+1

¿Podría hacerlo capturando la 'cadena s' original en un cierre? – asawyer

+1

@asawyer: De hecho, aunque es solo una forma elegante de decir "asignar una clase que tiene un campo de cadena, y luego almacenar una referencia a la clase". Si eso es lo que quieres hacer, hazlo. –

+0

@Eric Eso tiene mucho sentido. ¡Gracias! – asawyer

1

En esta línea:

this.parameter = parameter; 

... que copiar el parámetro del método a la miembro de la clase parameter. Luego, en Init(), está asignando el valor "éxito", de nuevo, al clase miembro parameter. En su Console.Writeline, está escribiendo el valor del parámetro del método, "failed", porque en realidad nunca modifica el parámetro del método.

Lo que estás tratando de hacer, la forma en que intentas hacerlo, no es posible, creo en C#. No trataría de pasar un string con el modificador ref.

1

Respondido por JaredPar, no se puede.

Pero el problema es en parte que la cuerda es inmutable. Cambie su parámetro para que sea de class Basket { public string status; } y su código funcionaría básicamente. No necesita la palabra clave ref, solo cambie parameter.status.

Y la otra opción es por supuesto Console.WriteLine("After: {0}", e.parameter);. Ajuste el parámetro en una propiedad (solo escritura).

+0

En general, no me gusta la sugerencia de que tenga algo que ver con la inmutabilidad de la cadena, que a menudo está presente para las preguntas que involucran asignaciones de cadenas. La reasignación no es una mutación, tipo mutable o no. Implica que tiene algo que ver con la inmutabilidad es una distorsión, ¿no estás de acuerdo? (Aunque la sugerencia de encapsular el valor en una clase es válida.) –

+2

No es este viejo perro otra vez ... Es solo una reasignación * porque * las cadenas son inmutables. – Steve

+0

@Steve, para ser claros, debes * usar * reasignación * porque * no puedes mutar la cadena original. Pero mi punto más importante es que la asignación es asignación, el tipo en sí mismo podría ser mutable o inmutable y el comportamiento de asignación 'x = y;' sería el mismo. El punto más grande es que mi lectura de la pregunta es que el tipo en sí mismo era irrelevante, quería mantener una referencia o alias a la variable original aprobada. Con ese fin, todas las respuestas confirman que no es directamente posible, pero han ofrecido formas de trabajar dentro del sistema de tipos para lograr el objetivo general. –

14

Como otros han señalado, no se puede almacenar una referencia a una variable en un campo en C#, o de hecho, cualquier lenguaje CLR.

Por supuesto que puede capturar una referencia a una instancia de la clase que contiene una variable con bastante facilidad:

sealed class MyRef<T> 
{ 
    public T Value { get; set; } 
} 
public class End 
{ 
    public MyRef<string> parameter; 
    public End(MyRef<string> parameter) 
    { 
    this.parameter = parameter; 
    this.Init(); 
    Console.WriteLine("Inside: {0}", parameter.Value); 
    } 
    public void Init() 
    { 
    this.parameter.Value = "success"; 
    } 
} 
class MainClass 
{ 
    public static void Main() 
    { 
    MyRef<string> s = new MyRef<string>(); 
    s.Value = "failed"; 
    End e = new End(s); 
    Console.WriteLine("After: {0}", s.Value); 
    } 
} 

Peasy fácil.

+1

Tendré que leer esto dos veces. –

Cuestiones relacionadas