Ceci est une version simplifiée de certains de mes codes:
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; } }
Le problème est que l’erreur Struct member 'info' causes a cycle in the struct layout.
Je suis après struct comme le comportement de type valeur. Je pourrais simuler ceci en utilisant une classe et une fonction membre clone, mais je ne vois pas pourquoi je devrais en avoir besoin.
Comment cette erreur est-elle vraie? La récursion pourrait peut-être causer la construction pour toujours dans des situations similaires, mais je ne vois pas comment cela pourrait arriver dans ce cas. Vous trouverez ci-dessous des exemples qui devraient convenir si le programme comstack.
new info(1, 2); new info(1, 2, null); new info(1, 2, new info(3, 4));
modifier:
La solution que j’ai utilisée a été de faire “info” une classe au lieu d’une structure et de lui donner une fonction membre pour renvoyer une copie que j’ai utilisée lors de sa transmission. En effet, simuler le même comportement qu’une structure mais avec une classe.
J’ai également créé la question suivante en cherchant une réponse.
Définition de classe de type de valeur en C #?
Ce n’est pas légal d’avoir une structure qui se contient en tant que membre. En effet, une structure a une taille fixe et doit être au moins aussi grande que la sum des tailles de chacun de ses membres. Votre type doit avoir 8 octets pour les deux flottants, au moins un octet pour indiquer si les info
sont non info
ou non, plus la taille d’une autre info
. Cela donne l’inégalité suivante:
size of info >= 4 + 4 + 1 + size of info
Ceci est évidemment impossible car cela exigerait que votre type soit infiniment grand.
Vous devez utiliser un type de référence (c.-à-d. Classe). Vous pouvez rendre votre classe immuable et remplacer Equals
et GetHashCode
pour donner un comportement semblable à la valeur, similaire à la classe Ssortingng
.
La raison pour laquelle cela crée un cycle est que Nullable
est lui-même une struct
. Parce qu’il renvoie à info
vous avez un cycle dans la présentation ( info
a un champ Nullable
et il a un champ info
). C’est essentiellement équivalent à ce qui suit
public struct MyNullable { public T value; public bool hasValue; } struct info { public float a, b; public MyNullable next; }
Le vrai problème est sur cette ligne:
public info? c;
Puisqu’il s’agit d’une struct
, C # a besoin de connaître la présentation des info
internes avant de pouvoir produire la présentation des info
externes. Et les info
internes incluent des info
internes, qui à leur tour incluent des info
internes, et ainsi de suite. Le compilateur ne peut pas produire de mise en page à cause de ce problème de référence circulaire.
Note: info? c
info? c
est un raccourci pour Nullable
qui est lui-même une struct
.
Il n’existe aucun moyen d’obtenir une sémantique de valeur variable d’éléments de taille variable (je pense que vous voulez que MyInfo1 = MyInfo2
génère une nouvelle liste chaînée qui est détachée de celle lancée par MyInfo2). On pourrait remplacer l’ info?
avec un info[]
(qui serait toujours soit nul, soit rempli avec un tableau à un seul élément), ou avec une classe de titulaire qui encapsule une instance d’ info
, mais la sémantique ne serait probablement pas ce que vous recherchez. Après MyInfo1 = MyInfo2
, les modifications apscopes à MyInfo1.a
n’affecteraient pas MyInfo2.a
, ni les modifications apscopes à MyInfo1.c
n’affecteraient pas MyInfo2.c
, mais les modifications apscopes à MyInfo1.c[0].a
auraient une incidence sur MyInfo2.c[0].a
.
Ce serait bien si une version future de .net pouvait avoir un concept de “références de valeur”, de sorte que la copie d’une structure ne copie pas simplement tous ses champs. Le fait que .net présente une certaine valeur n’est pas compatible avec toutes les subtilités des constructeurs de copie C ++, mais il serait également utile de permettre aux emplacements de stockage de type ‘struct’ d’avoir une identité qui serait associée à l’emplacement de stockage plutôt qu’à son contenu.
Étant donné que .net ne supporte actuellement aucun de ces concepts, cependant, si vous voulez que les info
soient mutables, vous devrez supporter soit la sémantique de référence mutable (y compris le clonage protecteur), soit la bizarre et délirante struct-class- sémantique hybride. Une suggestion que j’aurais si la performance est une préoccupation serait d’avoir une classe abstraite InfoBase
avec les descendants MutableInfo
et MutableInfo
, et avec les membres suivants:
AsNewFullyMutable
– AsNewFullyMutable
publique – Retourne un nouvel object MutableInfo
, avec les données copiées à partir de l’original, appelant AsNewFullyMutable
sur toutes les références nestedes.
AsNewMutable
– AsNewMutable
publique – Retourne un nouvel object MutableInfo
, avec des données copiées à partir de l’original, appelant AsImmutable
sur toutes les références nestedes.
AsNewImmutable
– AsNewImmutable
protégée – Retourne un nouvel object ImmutableInfo
, avec les données copiées à partir de l’origine, appelant AsImmutable
(pas AsNewImmutable
) sur les références nestedes.
AsImmutable
– Public virtual – Pour un ImmutableInfo
, retourne lui-même; pour un MutableInfo
, appelez AsNewImmutable
sur lui-même.
AsMutable
– Public virtuel – Pour un MutableInfo
, renvoie lui-même; pour un ImmutableInfo
, appelez AsNewMutable
sur lui-même.
Lors du clonage d’un object, on peut appeler AsImmutable
, AsNewFullyMutable
ou AsNewMutable
, selon que l’on s’attend à ce que l’object ou ses descendants soient à nouveau clonés avant d’être mutés. Dans les scénarios dans lesquels on s’attendrait à ce qu’un object soit cloné de manière défensive de manière répétée, l’object serait remplacé par une instance immuable qui ne devrait plus être clonée tant que personne ne souhaitait le muter.