¿Por qué C # no admite la devolución de referencias?

He leído que .NET admite el retorno de referencias, pero C # no. ¿Hay alguna razón especial? Por qué no puedo hacer algo como esto:

static ref int Max(ref int x, ref int y) { if (x > y) return ref x; else return ref y; } 

Esta pregunta fue el tema de mi blog el 23 de junio de 2011 . Gracias por la gran pregunta!

El equipo de C # está considerando esto para C # 7. Consulte https://github.com/dotnet/roslyn/issues/5233 para más detalles.

ACTUALIZACIÓN: ¡La función llegó a C # 7!


Estás en lo correcto; .NET admite métodos que devuelven referencias administradas a variables. .NET también admite variables locales que contienen referencias administradas a otras variables. (Sin embargo, tenga en cuenta que .NET no admite campos o matrices que contienen referencias administradas a otras variables porque eso complica demasiado la historia de la recolección de basura. Además, los tipos de “referencia gestionada a variable” no son convertibles a objeto , y por lo tanto no pueden usarse como escriba argumentos a tipos o métodos generics).

El comentarista “RPM1984” por alguna razón solicitó una cita para este hecho. RPM1984 Lo invito a leer la especificación CLI Partición I Sección 8.2.1.1, “Punteros administrados y tipos relacionados” para obtener información sobre esta característica de .NET.

Es completamente posible crear una versión de C # que admita estas dos características. Entonces podrías hacer cosas como

 static ref int Max(ref int x, ref int y) { if (x > y) return ref x; else return ref y; } 

y luego llámalo con

 int a = 123; int b = 456; ref int c = ref Max(ref a, ref b); c += 100; Console.WriteLine(b); // 556! 

Sé empíricamente que es posible construir una versión de C # que admita estas características porque ya lo hice . Los progtwigdores avanzados, particularmente las personas que portan código C ++ no administrado, a menudo nos piden más capacidad de C ++ para hacer cosas con referencias sin tener que sacar el gran martillo de usar punteros y fijar la memoria en todo el lugar. Al utilizar referencias administradas obtiene estos beneficios sin pagar el costo de arruinar su rendimiento de recolección de basura.

Hemos considerado esta característica, y de hecho implementamos suficiente para mostrarla a otros equipos internos para obtener sus comentarios. Sin embargo, en este momento, en función de nuestra investigación , creemos que la función no tiene un atractivo lo suficientemente amplio o casos de uso convincentes para convertirla en una función de idioma real . Tenemos otras prioridades más altas y una cantidad limitada de tiempo y esfuerzo disponible, por lo que no vamos a hacer esta función en el corto plazo.

Además, hacerlo correctamente requeriría algunos cambios en el CLR. En este momento, el CLR trata los métodos de devolución de referencias como legales pero no verificables porque no tenemos un detector que detecte esta situación:

 ref int M1(ref int x) { return ref x; } ref int M2() { int y = 123; return ref M1(ref y); // Trouble! } int M3() { ref int z = ref M2(); return z; } 

M3 devuelve el contenido de la variable local de M2, pero la vida útil de esa variable ha finalizado. Es posible escribir un detector que determine el uso de ref-returns que claramente no violan la seguridad de la stack. Lo que haríamos sería escribir un detector de este tipo, y si el detector no pudiera demostrar la seguridad de la stack, no permitiríamos el uso de declaraciones de ref en esa parte del progtwig. No es una gran cantidad de trabajo de desarrollo para hacerlo, pero es una gran carga para los equipos de prueba para asegurarnos de que realmente tenemos todos los casos. Es solo otra cosa que aumenta el costo de la función hasta el punto en que en este momento los beneficios no superan los costos.

Si puede describirme por qué desea esta función, realmente lo agradecería . Cuanta más información tengamos de clientes reales sobre por qué la quieren, más probable será que algún día llegue al producto. Es una pequeña característica linda y me gustaría poder llegar a los clientes de alguna manera si hay suficiente interés.

(Ver también preguntas relacionadas ¿Es posible devolver una referencia a una variable en C #? ¿Puedo usar una referencia dentro de una función C # como C ++? )

Está hablando de métodos que devuelven una referencia a un tipo de valor. El único ejemplo incorporado en C # que conozco es el acceso de matriz de un tipo de valor:

 public struct Point { public int X { get; set; } public int Y { get; set; } } 

y ahora crea una matriz de esa estructura:

 var points = new Point[10]; points[0].X = 1; points[0].Y = 2; 

En este caso, los points[0] , el indexador de matriz, devuelve una referencia a struct. Es imposible escribir su propio indexador (por ejemplo, para una colección personalizada), que tiene el mismo comportamiento de “devolver una referencia”.

No diseñé el lenguaje C #, así que no conozco todo el razonamiento detrás de no apoyarlo, pero creo que la respuesta corta podría ser: podemos llevarnos bien sin eso.

Siempre puedes hacer algo como:

 public delegate void MyByRefConsumer(ref T val); public void DoSomethingWithValueType(MyByRefConsumer c) { int x = 2; c(ref x); //Handle potentially changed x... } 

C # 7.0 tiene soporte para devolver referencias. Vea mi respuesta aquí .