【问题标题】:c# conversion operator for class with recursive collection具有递归集合的类的c#转换运算符
【发布时间】:2017-05-31 12:35:00
【问题描述】:

我一直在努力让演员为一个拥有自己集合的班级工作。在使用 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;
}

【问题讨论】:

  • 我没有看到这段代码会如何创建一个无限循环。你能用简化的代码重现这个问题吗?如果是这样,您是否可以包含构建 TypeA 类的代码,当您尝试转换它时会进入无限循环?此外,此代码示例中的任何地方都没有递归。
  • 我认为发生在 children.Add(item) (从 TypeA 项目到 Children)的隐式强制转换运算符再次调用自身以执行隐式转换将算作递归。但是,您如何称呼具有自身成员或自身成员集合的类(可能不是递归的 - 只是真的很好奇)?
  • 您没有TypeA 的集合,您有TypeB 的集合。如果您确实有一个正在转换的 TypeA 集合,那么您将进行递归,并且如果两个对象都在集合中包含另一个对象甚至它们自己,则您可能会得到一个无限循环。
  • 糟糕——我确实把事情搞砸了。它应该是 TypeA 的列表。
  • 如果是TypeA 的集合,您需要通过创建从TypeATypeAIntermediate 的映射来跟踪哪些对象已被转换,然后您可以填充集合通过在地图中查找转换创建的每个 TypeAIntermediate。假设你想在你的层次结构中保持循环。

标签: c# recursion casting implicit


【解决方案1】:

如果你的层次结构中有循环,你可以这样写以避免堆栈溢出。

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 方法。

此外,如果您在不同的分支中有两次相同的对象引用,则只会创建一个 TypeAIntermediate 引用而不是两个。

【讨论】:

  • 所以我可以确认我们只在这个类的三个实例上运行。根上的 TypeA 列表中的根和另外两个(都具有空的 TypeA 列表)。我还确认转换运算符仅在一个地方被调用,首先通过注释掉使用我的断点在运算符中进行强制转换的行(未达到),然后重新启用该行做一个断点的演员表。它只到达过一次。 ...但奇怪的循环行为仍然存在于转换代码中。
  • 我想我将不得不实际开始尝试简化代码。要回答您的问题,不,我没有尝试使用此版本的代码(但它与真实的东西非常相似)。我只是希望有人会立即认识到这个问题。不过,它自己的收藏中的根是一个很好的选择——我希望你有它。
  • 所以...我在一个空白项目中构建了原始简化代码...它运行良好。我猜我的项目中正在发生一些奇怪的事情。 ...根据发布的代码(因为我猜它有效),我的问题不会有一个成功的答案,所以也许这个转换循环检测器对其他人有用。谢谢。
猜你喜欢
  • 2021-07-09
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
  • 1970-01-01
  • 1970-01-01
  • 2012-01-27
  • 1970-01-01
  • 2013-09-17
相关资源
最近更新 更多