Escribí una prueba unitaria y usé el nuevo registro de C# para almacenar algunos datos que necesitaba para la prueba. La prueba unitaria funcionó bien, pero cuando configuré un punto de interrupción y moví el mouse sobre el nombre de la variable de registro, la sesión de depuración finalizó y recibí un mensaje de error de aspecto extraño.
Para informar el problema a Microsoft, escribí una prueba de unidad simple, que demuestra el problema, pero de lo contrario no tiene mucho sentido:
#nullable enable using Microsoft.VisualStudio.TestTools.UnitTesting; namespace CSharpRecordTest { record Twin(string Name) { public Twin? OtherTwin { get; set; } } [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { var twinA = new Twin("A"); var twinB = new Twin("B"); twinA.OtherTwin = twinB; twinB.OtherTwin = twinA; Assert.AreEqual(twinA, twinA.OtherTwin.OtherTwin); } } }La prueba funciona bien, pero al establecer un punto de interrupción en Assert.AreEqual( y mover el mouse sobre twinA, la sesión de depuración se detiene y aparece este mensaje de error:
Me tomó uno o dos días darme cuenta de lo que estaba pasando, porque el depurador no me dejaba ver nada. Escribí una aplicación de consola:
class Program { record Twin(string Name) { public Twin? OtherTwin { get; set; } } static void Main(string[] args) { var twinA = new Twin("A"); var twinB = new Twin("B"); twinA.OtherTwin = twinB; twinB.OtherTwin = twinA; Console.WriteLine(twinA); } }La aplicación de la consola también tuvo problemas, pero al menos mostró que hubo un desbordamiento de la pila y qué línea causó:
Console.WriteLine(twinA);La pila se veía así (aunque más complicada):
twinA.OtherTwin.OtherTwin.OtherTwin.OtherTwin.OtherTwin.OtherTwin...OtherTwin.ToString()El problema es que ToString(), que el compilador escribe automáticamente, vuelve a llamar a .ToString() en cada propiedad.
Si un registro hace referencia a sí mismo o a otros registros que luego hacen referencia al primer registro, ToString() fallará y debe anularse junto con Equals(), GetHashCode() y tal vez más (no lo probé).
En realidad, cuando se tiene una propiedad que no es de solo lectura, Equals() y GetHashCode() deben anularse; de lo contrario, las clases como Dictionary no funcionarán.
Para obtener más detalles, consulte mi artículo sobre CodeProject: Registro de C# 9: el código ToString() creado por el compilador puede provocar un desbordamiento de la pila y cosas peores