具有递归集合的类的c#转换运算符

时间:2017-05-31 12:35:01

标签: c# recursion casting implicit

我一直在努力让演员为一个有自己收藏的班级工作。在使用在List中具有两个TypeA元素的根对象进行测试时,当List执行隐式转换时...它输入集合的TypeA元素的转换代码,并且因为这是树的顶部,所以返回TypeAIntermediate没有进入foreach循环(完美 - SomeAs中什么也没有。但是当它返回转换后的实例时,它似乎从根目录转换代码的顶部重新开始,就像没有发生任何事情一样。

据我所知,这永远不会停止。我重写了这个简化版本,遵循相同的格式...希望我没有搞砸。

//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;
}

1 个答案:

答案 0 :(得分:0)

Here's how you can write that to avoid stack overflow if you have loops in your hierarchy.

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;
}

Basically by using a dictionary you can determine if any TypeA object already has a TypeAIntermediate object created for it in which case you don't need to create it again and just return the corresponding TypeAIntermediate reference. If it hasn't then you create a new TypeAIntermediate and populate it's collection be recursively calling the Create method that also takes the dictionary.

Additionally if you have the same object reference twice in different branches this will only create one TypeAIntermediate reference instead of two.