2017-05-31 1 views
0

J'ai eu du mal à faire fonctionner une distribution pour une classe qui a une collection de lui-même. Dans le test avec un objet racine qui a deux éléments TypeA dans la liste, lorsque la liste fait le cast implicite ... il entre le code de conversion pour l'élément TypeA de la collection, et parce que c'est le haut de l'arbre, renvoie TypeAIntermediate sans entrer dans la boucle de foreach (ce parfait - il n'y a rien dans SomeAs). Mais quand il retourne l'instance convertie, il semble recommencer en haut du code de conversion pour la racine comme si rien ne s'était passé. Autant que je sache, cela ne s'arrête jamais. J'ai réécrit cette version simplifiée qui suit le même format ... j'espère que je n'ai pas gâché.Opérateur de conversion C# pour une classe avec une collection récursive

//These are models used in a .Net 4.5 EF6 Library 
public class TypeA 
{ 
    public string TypeAStuff; 
    public TypeB JustOneB; 
    public List<TypeA> SomeAs; 


    public static implicit operator TypeAIntermediate(TypeA a) 
    { 
     //New up an Intermediate A to return. 
     TypeAIntermediate aI = new TypeAIntermediate(); 
     //And get ready to do handle the collection... a few ways to do this. 
     List<TypeAIntermediate> children = new List<TypeAIntermediate>(); 

     //...but this appears to create an infinite loop? 
     foreach (TypeA item in a.SomeAs) 
      children.Add(item); //Cast from TypeA to to TypeAIntermediate happens here but will just keeps cycling 

     aI.TypeAStuff = a.TypeAStuff; 
     aI.JustOneB = a.JustOneB; 
     aI.SomeAs = children; 
     return aI; 
    } 
} 

public class TypeB 
{ 
    public string TypeBStuff; 

    public static implicit operator TypeBIntermediate(TypeB b) 
    { 
     TypeBIntermediate bI = new TypeBIntermediate(); 
     bI.TypeBStuff = b.TypeBStuff; 
     return bI; 
    } 
} 

//These Intermediate Classes live in a .Net35 Library - Unity cannot use Libraries compiled for later .Net Versions. 
public class TypeAIntermediate 
{ 
    public string TypeAStuff; 
    public TypeBIntermediate JustOneB; 
    public List<TypeAIntermediate> SomeAs; 
} 

public class TypeBIntermediate 
{ 
    public string TypeBStuff; 
} 
+0

Je ne vois pas comment ce code créerait une boucle infinie. Pouvez-vous reproduire le problème avec le code simplifié? Si oui, pouvez-vous inclure le code qui construit la classe 'TypeA' qui se trouve dans une boucle infinie lorsque vous essayez de le convertir? Il n'y a pas non plus de récursivité dans cet exemple de code. – juharr

+0

Je pense que la distribution implicite qui se produit à children.Add (item) (de TypeA item à Children ) qui force l'opérateur de conversion à se rappeler de faire le casting implicite compterait comme récursivité. Mais aussi, qu'est-ce que vous appelez une classe qui a des membres de lui-même, ou des collections de membres de lui-même (peut-être pas récursif - juste en fait curieux)? –

+0

Vous n'avez pas une collection de 'TypeA' vous avez une collection de' TypeB'. Si vous aviez une collection de 'TypeA' qui était convertie alors vous auriez une récursivité et vous pourriez obtenir une boucle infinie si deux objets avaient l'autre ou même eux-mêmes à l'intérieur de la collection. – juharr

Répondre

0

Voici comment vous pouvez écrire cela pour éviter le débordement de pile si vous avez des boucles dans votre hiérarchie.

public static implicit operator TypeAIntermediate(TypeA a) 
{ 
    return Convert(a, new Dictionary<TypeA, TypeAIntermediate>()); 
} 

private static TypeAIntermediate Convert(
    Type A a, 
    Dictionary<TypeA, TypeAIntermediate> lookup) 
{ 
    TypeAIntermediate aI; 
    if(lookup.TryGetValue(a, out aI)) 
    { 
     return aI; 
    } 

    aI = new TypeAintermediate(); 
    lookup.Add(a, aI); 

    List<TypeAIntermediate> children = new List<TypeAIntermediate>(); 
    foreach (TypeA item in a.SomeAs) 
     children.Add(Convert(item, lookup)); 

    aI.TypeAStuff = a.TypeAStuff; 
    aI.JustOneB = a.JustOneB; 
    aI.SomeAs = children; 
    return aI; 
} 

En fait à l'aide d'un dictionnaire, vous pouvez déterminer si un objet TypeA a déjà un objet TypeAIntermediate créé pour elle dans ce cas, vous n'avez pas besoin de le créer à nouveau et juste renvoyer la référence TypeAIntermediate correspondante. Si ce n'est pas le cas, vous devez créer un nouveau TypeAIntermediate et remplir sa collection en appelant de manière récursive la méthode Create qui prend également le dictionnaire.

De plus, si vous avez deux fois la même référence d'objet dans différentes branches, cela créera une seule référence TypeAIntermediate au lieu de deux.

+0

Je peux donc confirmer que nous ne fonctionnons que sur trois instances de cette classe.La racine et deux autres (les deux avec des listes TypeA vides) qui sont dans la liste des types sur la racine. J'ai également confirmé que l'opérateur de conversion est seulement appelé en un seul endroit, d'abord en commentant la ligne qui fait la distribution avec mon point de rupture dans l'alambic dans l'opérateur (il n'a pas été atteint), puis en réactivant la ligne fait le casting avec un point de rupture dessus. C'était seulement atteint une fois. ... mais le comportement de la boucle impaire a persisté dans le code de conversion. –

+0

J'imagine que je vais devoir commencer à expérimenter avec le code simplifié. Pour répondre à ta question, non je ne l'ai pas essayé avec cette version du code (mais c'est assez similaire au vrai). J'espérais juste que quelqu'un reconnaîtrait immédiatement le problème. La racine de sa propre collection était un bon appel - j'espérais que vous l'aviez. –

+0

Alors ... J'ai construit le code simplifié original dans un projet vide ... ça marche bien. Quelque chose d'étrange se passe dans mon projet, je suppose. ... Il n'y aura pas de réponse réussie à ma question basée sur le code affiché (puisque je suppose que cela fonctionne) alors peut-être que cette chose de détecteur de boucle de conversion sera utile à quelqu'un d'autre. Merci. –