2017-05-31 189 views
0

我一直在努力獲得一個演員工作的類具有自己的集合。在使用List中具有兩個TypeA元素的根對象進行測試時,當List執行隱式轉換時...它將輸入集合的TypeA元素的轉換代碼,並且因爲這是樹的頂部,所以返回TypeAIntermediate無需進入foreach循環(即完美 - SomeAs中沒有任何內容)。但是當它返回轉換的實例時,它似乎重新開始在根的轉換代碼的頂部,就像什麼都沒有發生。c#轉換運算符爲遞歸集合類

據我所知,這永遠不會停止。我重寫了這個遵循相同格式的簡化版本......希望我沒有搞砸。

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

我沒有看到這段代碼如何創建一個無限循環。你能用簡化的代碼重現問題嗎?如果是的話,你是否可以包含構建'TypeA'類的代碼,當你嘗試轉換它時會進入無限循環?此代碼示例中也沒有任何地方遞歸。 – juharr

+0

我認爲隱式強制轉換髮生在children.Add(item)(從TypeA項到Children )強制轉換運算符再次調用自己以執行隱式強制轉換將計爲遞歸。而且,你怎麼稱呼一個擁有自身成員的類,或者自己的成員集合(可能不是遞歸的 - 實際上好奇)? –

+0

您沒有'TypeA'集合,您有'TypeB'集合。如果你確實有一個正在被轉換的'TypeA'的集合,那麼你將會有遞歸,如果兩個對象都擁有另一個或者它們本身在集合中,你可以得到一個無限循環。 – juharr

回答

0

下面是如何編寫該方法以避免堆棧溢出,如果您的層次結構中存在環路。

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

基本上通過使用字典,你可以確定是否有任何TypeA對象已經爲它創造一個TypeAIntermediate對象在這種情況下,你不需要再次創建它,只是返回相應TypeAIntermediate參考。如果它沒有,那麼你創建一個新的TypeAIntermediate並填充它的集合遞歸調用Create方法,也需要字典。

此外,如果您在不同分支中有兩次相同的對象引用,則只會創建一個參考而不是兩個參考。

+0

所以我可以確認我們只對這個類的三個實例進行操作。根和另外兩個(均具有空的TypeA列表),它們位於根目錄的TypeA列表中。我也確認轉換操作符只在一個地方被調用,首先通過註釋掉在操作符中仍然存在斷點的線條(未到達),然後重新啓用這條線在演員身上有一個突破點。它只能達到一次。 ...但奇怪的循環行爲持續在轉換代碼中。 –

+0

我想我必須真正開始嘗試簡化的代碼。爲了回答你的問題,不,我沒有試圖用這個版本的代碼(但它與真實的東西非常相似)。我只是希望有人能立即認識到這個問題。它自己收藏的根本是一個很好的電話 - 我希望你能擁有它。 –

+0

所以......我在一個空白的項目中構建了原始的簡化代碼......它運行良好。我想我的項目中有一些陌生人正在進行。 ...根據發佈的代碼,我的問題不會有成功的答案(因爲我猜測它的工作原理),所以也許這個轉換循環檢測器的東西對其他人有用。謝謝。 –