using NUnit.Framework; using System; using System.Text; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Collections; using System.Collections.Generic; namespace LeanCloud.Test { public class LCObject { public string Id { get; set; } public Dictionary Data { get; set; } public LCObject(string id) { Data = new Dictionary(); Id = id; } public static Stack Batch(IEnumerable objects) { Stack batches = new Stack(); IEnumerable deps = objects; do { // 只添加本层依赖的 LCObject IEnumerable avObjects = deps.OfType(); if (avObjects.Any()) { batches.Push(new Batch(avObjects)); } HashSet childSets = new HashSet(); foreach (object dep in deps) { IEnumerable children = null; if (dep is IList) { children = dep as IList; } else if (dep is IDictionary) { children = dep as IDictionary; } else if (dep is LCObject) { children = (dep as LCObject).Data.Values; } if (children != null) { foreach (object child in children) { childSets.Add(child); } } } deps = childSets; } while (deps != null && deps.Any()); return batches; } public static bool HasCircleReference(object obj, HashSet parents) { if (parents.Contains(obj)) { return true; } IEnumerable deps = null; if (obj is IList) { deps = obj as IList; } else if (obj is IDictionary) { deps = (obj as IDictionary).Values; } else if (obj is LCObject) { deps = (obj as LCObject).Data.Values; } HashSet depParent = new HashSet(parents); if (obj is LCObject) { depParent.Add((LCObject) obj); } if (deps != null) { foreach (object dep in deps) { HashSet set = new HashSet(depParent); if (HasCircleReference(dep, set)) { return true; } } } return false; } public Stack Batch() { return Batch(new List { this }); } public bool HasCircleReference() { return HasCircleReference(this, new HashSet()); } } public class Batch { HashSet ObjectSet { get; set; } public Batch() { ObjectSet = new HashSet(); } public Batch(IEnumerable objects) : this() { foreach (LCObject obj in objects) { ObjectSet.Add(obj); } } public override string ToString() { StringBuilder sb = new StringBuilder(); sb.AppendLine("----------------------------"); foreach (LCObject obj in ObjectSet) { sb.AppendLine(obj.Id); } sb.AppendLine("----------------------------"); return sb.ToString(); } } public class AVObjectTest { void PrintBatches(Stack batches) { while (batches.Any()) { Batch batch = batches.Pop(); TestContext.WriteLine(batch); } } [Test] public void Simple() { LCObject a = new LCObject("a"); LCObject b = new LCObject("b"); LCObject c = new LCObject("c"); a.Data["child"] = b; b.Data["child"] = c; Assert.IsFalse(a.HasCircleReference()); Stack batches = a.Batch(); PrintBatches(batches); } [Test] public void Array() { LCObject a = new LCObject("a"); LCObject b = new LCObject("b"); LCObject c = new LCObject("c"); a.Data["children"] = new List { b, c }; Assert.IsFalse(a.HasCircleReference()); Stack batches = a.Batch(); PrintBatches(batches); } [Test] public void SimpleCircleReference() { LCObject a = new LCObject("a"); LCObject b = new LCObject("b"); a.Data["child"] = b; b.Data["child"] = a; Assert.IsTrue(a.HasCircleReference()); } [Test] public void ComplexCircleReference() { LCObject a = new LCObject("a"); LCObject b = new LCObject("b"); LCObject c = new LCObject("c"); a.Data["arr"] = new List { 1, b }; a.Data["child"] = c; b.Data["arr"] = new List { 2, a }; Assert.IsTrue(a.HasCircleReference()); } [Test] public void ComplexCircleReference2() { LCObject a = new LCObject("a"); LCObject b = new LCObject("b"); List list = new List(); a.Data["list"] = list; b.Data["list"] = list; a.Data["child"] = b; Assert.IsFalse(a.HasCircleReference()); } } }