visual studio ejemplos data array c# constructor struct member cyclic-reference

c# - studio - Ciclo en el diseño de la estructura que no existe



struct constructor c# (4)

El verdadero problema está en esta línea:

public info? c;

Ya que esta es una struct , C # necesita conocer el diseño de la info interna antes de que pueda producir el diseño de la info externa. Y la info interna incluye una información interna interna, que a su vez incluye una info interna interna, y así sucesivamente. El compilador no puede producir un diseño debido a este problema de referencia circular.

Nota: info? c info? c es una abreviatura de Nullable<info> que es en sí misma una struct .

Esta es una versión simplificada de algunos de mis códigos:

public struct info { public float a, b; public info? c; public info(float a, float b, info? c = null) { this.a = a; this.b = b; this.c = c; } }

El problema es que el error Struct member ''info'' causes a cycle in the struct layout. Estoy detrás de la estructura como el tipo de comportamiento de valor. Podría simular esto utilizando una clase y una función de miembro clon, pero no veo por qué debería necesitarlo.

¿Cómo es este error verdadero? La recursión quizás podría causar la construcción para siempre en situaciones similares, pero no puedo pensar en ninguna otra forma en este caso. Abajo hay ejemplos que deberían estar bien si el programa se compilara.

new info(1, 2); new info(1, 2, null); new info(1, 2, new info(3, 4));

editar:

La solución que utilicé fue hacer de "información" una clase en lugar de una estructura y asignarle una función miembro para que devolviera una copia que usé cuando la pasé. En efecto simulando el mismo comportamiento que una estructura pero con una clase.

También creé la siguiente pregunta mientras buscaba una respuesta.

¿Definición de clase de tipo de valor en C #?


La razón por la que esto crea un ciclo es que Nullable<T> es en sí mismo una struct . Debido a que hace referencia a la info , tiene un ciclo en el diseño (la info tiene un campo de Nullable<info> y tiene un campo de info ). Es esencialmente equivalente a lo siguiente

public struct MyNullable<T> { public T value; public bool hasValue; } struct info { public float a, b; public MyNullable<info> next; }


No es legal tener una estructura que se contenga como miembro. Esto se debe a que una estructura tiene un tamaño fijo y debe ser al menos tan grande como la suma de los tamaños de cada uno de sus miembros. Su tipo tendría que tener 8 bytes para los dos flotantes, al menos un byte para mostrar si la info es nula o no, más el tamaño de otra info . Esto da la siguiente desigualdad:

size of info >= 4 + 4 + 1 + size of info

Esto es obviamente imposible, ya que requeriría que su tipo sea infinitamente grande.

Tienes que usar un tipo de referencia (es decir, clase). Puedes hacer que tu clase sea inmutable y anular a Equals y GetHashCode para dar un comportamiento similar al valor, similar a la clase String .


No hay ninguna manera de lograr una semántica de valores variables de elementos de tamaño variable (semánticamente, creo que lo que está buscando es que MyInfo1 = MyInfo2 genere una nueva lista vinculada que se separa de la iniciada por MyInfo2). ¿Se podría reemplazar la info? con una info[] (que siempre sería nula o bien rellena con una matriz de un solo elemento) o con una clase de titular que envuelve una instancia de info , pero la semántica probablemente no sea lo que está buscando. Siguiendo a MyInfo1 = MyInfo2 , los cambios en MyInfo1.a no afectarán a MyInfo2.a , ni los cambios a MyInfo1.c afectarán a MyInfo2.c , pero los cambios a MyInfo1.c[0].a afectarán a MyInfo2.c[0].a .

Sería bueno si una futura versión de .net pudiera tener algún concepto de "referencias de valor", de modo que copiar una estructura no copiaría simplemente todos sus campos. Hay cierto valor en el hecho de que .net no admite todas las complejidades de los constructores de copia de C ++, pero también sería valioso permitir que las ubicaciones de almacenamiento del tipo ''struct'' tengan una identidad que se asociaría con la ubicación de almacenamiento en lugar de su contenido.

Sin embargo, dado que .net actualmente no admite ningún concepto de este tipo, si desea que la info sea ​​mutable, tendrá que soportar la semántica de referencia mutable (incluida la clonación de protección) o la estructura de clase extraña y extravagante. Semántica híbrida. Una sugerencia que tendría si el rendimiento fuera una preocupación sería tener una clase abstracta de InfoBase con descendientes MutableInfo e MutableInfo , y con los siguientes miembros:

  1. AsNewFullyMutable - Public instance: devuelve un nuevo objeto MutableInfo , con datos copiados del original, llamando a AsNewFullyMutable en cualquier referencia anidada.

  2. AsNewMutable - Public instance: devuelve un nuevo objeto MutableInfo , con datos copiados del original, llamando a AsImmutable en cualquier referencia anidada.

  3. AsNewImmutable - Instancia protegida: devuelve un nuevo objeto ImmutableInfo , con datos copiados desde el origen, llamando a AsImmutable (no como AsNewImmutable ) en cualquier referencia anidada.

  4. AsImmutable - Public virtual - Para una ImmutableInfo , devuélvase; para un MutableInfo , llame a AsNewImmutable en sí mismo.

  5. AsMutable - Public virtual - Para un MutableInfo , devuélvase a sí mismo; para un AsNewMutable , llame a AsNewMutable en sí mismo.

Al clonar un objeto, dependiendo de si uno esperaba que el objeto o sus descendientes se clonaran de nuevo antes de ser mutado, uno llamaría AsImmutable , AsNewFullyMutable o AsNewMutable . En escenarios donde uno esperaría que un objeto fuera clonado defensivamente repetidamente, el objeto sería reemplazado por un ejemplo inmutable que ya no tendría que ser clonado hasta que hubiera un deseo de mutarlo.