// Generates a class declaration for the current alt and its subtypes public void GenerateOutput(ref VList <LNode> list) { bool isAbstract = _classAttrs.Any(a => a.IsIdNamed(S.Abstract)); var baseParts = new List <AdtParam>(); for (var type = ParentType; type != null; type = type.ParentType) { baseParts.InsertRange(0, type.Parts); } var allParts = baseParts.Concat(Parts); var initialization = Parts.Select(p => LNode.Call(CodeSymbols.Assign, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id(CodeSymbols.This), p.NameId)).SetStyle(NodeStyle.Operator), p.NameId)).SetStyle(NodeStyle.Operator)).ToList(); if (baseParts.Count > 0) { initialization.Insert(0, F.Call(S.Base, baseParts.Select(p => p.NameId))); } var args = new VList <LNode>(allParts.Select(p => p.OriginalDecl)); if (!_constructorAttrs.Any(a => a.IsIdNamed(S.Public))) { _constructorAttrs.Add(F.Id(S.Public)); } LNode constructor = LNode.Call(LNode.List(_constructorAttrs), CodeSymbols.Constructor, LNode.List(LNode.Missing, _typeNameStem, LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(initialization).AddRange(_extraConstrLogic)).SetStyle(NodeStyle.Statement))); var outBody = new VList <LNode>(); outBody.Add(constructor); outBody.AddRange(Parts.Select(p => p.GetFieldDecl())); outBody.AddRange(baseParts.Select(p => GetWithFn(p, isAbstract, S.Override, allParts))); outBody.AddRange(Parts.Select(p => GetWithFn(p, isAbstract, _children.Count > 0 ? S.Virtual : null, allParts))); outBody.AddRange(Parts.WithIndexes() .Where(kvp => kvp.Value.NameId.Name.Name != "Item" + (baseParts.Count + kvp.Key + 1)) .Select(kvp => kvp.Value.GetItemDecl(baseParts.Count + kvp.Key + 1))); outBody.AddRange(_classBody); list.Add(LNode.Call(LNode.List(_classAttrs), CodeSymbols.Class, LNode.List(TypeName, LNode.Call(CodeSymbols.AltList, LNode.List(BaseTypes)), LNode.Call(CodeSymbols.Braces, LNode.List(outBody)).SetStyle(NodeStyle.Statement)))); if (_genericArgs.Count > 0 && Parts.Count > 0) { var argNames = allParts.Select(p => p.NameId); list.Add(LNode.Call(LNode.List().AddRange(_classAttrs).Add(LNode.Id(CodeSymbols.Static)).Add(LNode.Id(CodeSymbols.Partial)), CodeSymbols.Class, LNode.List(_typeNameStem, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(LNode.List(LNode.Id(CodeSymbols.Public), LNode.Id(CodeSymbols.Static)), CodeSymbols.Fn, LNode.List(TypeNameWithoutAttrs, LNode.Call(CodeSymbols.Of, LNode.List().Add(LNode.Id((Symbol)"New")).AddRange(_genericArgs)).SetStyle(NodeStyle.Operator), LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(TypeNameWithoutAttrs, LNode.List(argNames)))))))).SetStyle(NodeStyle.Statement))))).SetStyle(NodeStyle.Statement)))); } foreach (var child in _children) { child.GenerateOutput(ref list); } }
public static IListSource <ILNode> GetTrailingTrivia(this ILNode node) { if (node is LNode) { LNodeList list = GetTrailingTrivia((LNode)node); if (list.IsEmpty) { return(EmptyList <ILNode> .Value); // avoid boxing in the common case } return(list); } else { VList <ILNode> list = VList <ILNode> .Empty; foreach (ILNode a in node.Attrs()) { if (a.Calls(S.TriviaTrailing)) { list.AddRange(a.Args()); } } if (list.IsEmpty) { return(EmptyList <ILNode> .Value); // avoid boxing in the common case } return(list); } }
public static VList <LNode> WithSpliced(this VList <LNode> list, LNode node, Symbol listName = null) { if (node.Calls(listName ?? CodeSymbols.Splice)) { return(list.AddRange(node.Args)); } else { return(list.Add(node)); } }
private void Add(ref VList <LNode> results, LNode result) { if (result.Calls(S.Splice)) { results.AddRange(result.Args); } else { results.Add(result); } }
private void LoadGroup(TGroup group, string groupName, TGrid grid) { VList <T> returnList = FetchInformation(group); if (Session[groupName] != null) { List <T> tempList = (List <T>)Session[groupName]; returnList.AddRange(tempList); } grid.DataSource = returnList; grid.DataBind(); }
public static LNode concatId(LNode node, IMessageSink sink) { var args = node.Args; if (args.Count == 0) { return(null); } if (args.Slice(0, args.Count - 1).Any(n => n.IsCall)) { return(Reject(sink, node, "All arguments to ##() or concat() must be identifiers or literals (except the last one)")); } VList <LNode> attrs = node.Attrs; LNode arg = null; StringBuilder sb = new StringBuilder(); for (int i = 0; i < args.Count; i++) { arg = args[i]; attrs.AddRange(arg.Attrs); if (arg.IsLiteral) { sb.Append(arg.Value ?? "null"); } else if (arg.IsId) { sb.Append(arg.Name); } else // call { if (i + 1 != args.Count || !arg.HasSimpleHead()) { return(Reject(sink, arg, "Expected simple identifier or literal")); } sb.Append(arg.Name); } } Symbol combined = GSymbol.Get(sb.ToString()); LNode result; if (arg.IsCall) { result = arg.WithTarget(combined); } else { result = LNode.Id(combined, node); } return(result.WithAttrs(attrs)); }
static bool MatchesPatternNested(LNode candidate, LNode pattern, ref MMap <Symbol, LNode> captures, ref VList <LNode> trivia) { VList <LNode> unmatchedAttrs; if (!MatchesPattern(candidate, pattern, ref captures, out unmatchedAttrs)) { return(false); } if (unmatchedAttrs.Any(a => !a.IsTrivia)) { return(false); } trivia.AddRange(unmatchedAttrs); return(true); }
public void ScanClassBody(VList <LNode> body) { foreach (var stmt in body) { int i; { LNode altName; VList <LNode> attrs, childBody = default(VList <LNode>), parts, rest; if ((attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fn, 3) && stmt.Args[0].IsIdNamed((Symbol)"alt") && (altName = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (parts = stmt.Args[2].Args).IsEmpty | true || (attrs = stmt.Attrs).IsEmpty | true && stmt.Calls(CodeSymbols.Fn, 4) && stmt.Args[0].IsIdNamed((Symbol)"alt") && (altName = stmt.Args[1]) != null && stmt.Args[2].Calls(CodeSymbols.AltList) && (parts = stmt.Args[2].Args).IsEmpty | true && stmt.Args[3].Calls(CodeSymbols.Braces) && (childBody = stmt.Args[3].Args).IsEmpty | true) { LNode genericAltName = altName; if (altName.CallsMin(CodeSymbols.Of, 1)) { } else if (_genericArgs.Count > 0) { genericAltName = LNode.Call(CodeSymbols.Of, LNode.List().Add(altName).AddRange(_genericArgs.ToVList())).SetStyle(NodeStyle.Operator); } var child = new AltType(attrs, genericAltName, LNode.List(), this); child.AddParts(parts); child.ScanClassBody(childBody); _children.Add(child); } else if ((attrs = stmt.Attrs).IsEmpty | true && (i = attrs.IndexWhere(a => a.IsIdNamed(__alt))) > -1 && stmt.CallsMin(CodeSymbols.Constructor, 3) && stmt.Args[1].IsIdNamed((Symbol)"#this") && stmt.Args[2].Calls(CodeSymbols.AltList) && (rest = new VList <LNode>(stmt.Args.Slice(3))).IsEmpty | true && rest.Count <= 1) { parts = stmt.Args[2].Args; attrs.RemoveAt(i); _constructorAttrs.AddRange(attrs); if (rest.Count > 0 && rest[0].Calls(S.Braces)) { _extraConstrLogic.AddRange(rest[0].Args); } AddParts(parts); } else { _classBody.Add(stmt); } } } }
static bool MatchesPatternNested(LNode candidate, LNode pattern, ref MMap<Symbol, LNode> captures, ref VList<LNode> trivia) { VList<LNode> unmatchedAttrs; if (!MatchesPattern(candidate, pattern, ref captures, out unmatchedAttrs)) return false; if (unmatchedAttrs.Any(a => !a.IsTrivia)) return false; trivia.AddRange(unmatchedAttrs); return true; }
void ProcessEnsuresAttribute(VList <LNode> conditions, Symbol mode, LNode exceptionType, LNode variableName) { // Create a "Contract.Whatever()" check for each provided condition. bool haveCCRewriter = _haveCCRewriter && mode != sy_ensuresAssert && mode != sy_ensuresFinally; var checks = LNode.List(); foreach (var condition_ in conditions) { LNode condition = condition_; // make it writable so we can replace `_` LNode conditionStr; LNode contractResult = null; string underscoreError = null; if (mode == sy_ensuresOnThrow) { contractResult = Id__exception__; if (haveCCRewriter) { underscoreError = "`ensuresOnThrow` does not support `_` in MS Code Contracts mode."; } } else // @@ensures or @@ensuresAssert or @@ensuresFinally { contractResult = haveCCRewriter ? LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"Result"), ReturnType)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator)) : Id_return_value; if (mode == sy_ensuresFinally) { underscoreError = "The macro for `{0}` does not support `_` because the return value is not available in `finally`"; } else if (haveCCRewriter && ReturnType.IsIdNamed(S.Missing)) { underscoreError = "The macro for `{0}` does not support `_` in this context when MS Code Contracts are enabled, because the return type is unknown."; } bool changed = ReplaceContractUnderscore(ref condition, contractResult); } if (ReplaceContractUnderscore(ref condition, contractResult) && underscoreError != null) { Context.Sink.Error(condition, underscoreError, mode); } if (haveCCRewriter) { if (mode == sy_ensuresOnThrow) { checks.Add(exceptionType != null ? LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Call(CodeSymbols.Of, LNode.List(LNode.Id((Symbol)"EnsuresOnThrow"), exceptionType)).SetStyle(NodeStyle.Operator))).SetStyle(NodeStyle.Operator), LNode.List(condition)) : LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Id((Symbol)"EnsuresOnThrow"))).SetStyle(NodeStyle.Operator), LNode.List(condition))); } else { checks.Add(LNode.Call(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id((Symbol)"Contract"), LNode.Id((Symbol)"Ensures"))).SetStyle(NodeStyle.Operator), LNode.List(condition))); } } else { conditionStr = ConditionToStringLit(condition, mode == sy_ensuresOnThrow ? "Postcondition failed after throwing an exception: {1}" : "Postcondition failed: {1}"); if (mode == sy_ensuresOnThrow) { var excType = GetExceptionTypeForEnsuresOnThrow(); checks.Add(LNode.Call(CodeSymbols.If, LNode.List(LNode.Call(CodeSymbols.Not, LNode.List(condition)).SetStyle(NodeStyle.Operator), LNode.Call(CodeSymbols.Throw, LNode.List(LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(excType, LNode.List(conditionStr, Id__exception__))))))))); } else { LNode assertMethod; if (mode == sy_ensuresAssert) { assertMethod = GetAssertMethod(Context); } else if (mode == sy_ensuresFinally) { assertMethod = GetAssertMethodForEnsuresFinally(); } else { assertMethod = GetAssertMethodForEnsures(); } checks.Add(LNode.Call(assertMethod, LNode.List(condition, conditionStr))); } } } // Request that the checks be added to the beginning of the method if (checks.Count > 0) { if (_haveCCRewriter) { PrependStmts.AddRange(checks); } else if (mode == sy_ensuresOnThrow) { LNode excSpec = exceptionType == null ? Id__exception__ : LNode.Call(CodeSymbols.Var, LNode.List(exceptionType, Id__exception__)); PrependStmts.Add(LNode.Call((Symbol)"on_throw", LNode.List(excSpec, LNode.Call(CodeSymbols.Braces, LNode.List(checks)).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special)); } else if (mode == sy_ensuresFinally) { PrependStmts.Add(LNode.Call((Symbol)"on_finally", LNode.List(LNode.Call(CodeSymbols.Braces, LNode.List(checks)).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special)); } else // mode == @@ensures || mode == @@ensuresAssert { PrependStmts.Add(LNode.Call((Symbol)"on_return", LNode.List(Id_return_value, LNode.Call(CodeSymbols.Braces, LNode.List(checks)).SetStyle(NodeStyle.Statement))).SetStyle(NodeStyle.Special)); } } }
// Generates a class declaration for the current alt and its subtypes public void GenerateOutput(ref VList<LNode> list) { bool isAbstract = _classAttrs.Any(a => a.IsIdNamed(S.Abstract)); var baseParts = new List<AdtParam>(); for (var type = ParentType; type != null; type = type.ParentType) baseParts.InsertRange(0, type.Parts); var allParts = baseParts.Concat(Parts); var initialization = Parts.Select(p => LNode.Call(CodeSymbols.Assign, LNode.List(LNode.Call(CodeSymbols.Dot, LNode.List(LNode.Id(CodeSymbols.This), p.NameId)).SetStyle(NodeStyle.Operator), p.NameId)).SetStyle(NodeStyle.Operator)).ToList(); if (baseParts.Count > 0) initialization.Insert(0, F.Call(S.Base, baseParts.Select(p => p.NameId))); var args = new VList<LNode>(allParts.Select(p => p.OriginalDecl)); if (!_constructorAttrs.Any(a => a.IsIdNamed(S.Public))) _constructorAttrs.Add(F.Id(S.Public)); LNode constructor = LNode.Call(LNode.List(_constructorAttrs), CodeSymbols.Constructor, LNode.List(LNode.Missing, _typeNameStem, LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List().AddRange(initialization).AddRange(_extraConstrLogic)).SetStyle(NodeStyle.Statement))); var outBody = new VList<LNode>(); outBody.Add(constructor); outBody.AddRange(Parts.Select(p => p.GetFieldDecl())); outBody.AddRange(baseParts.Select(p => GetWithFn(p, isAbstract, S.Override, allParts))); outBody.AddRange(Parts.Select(p => GetWithFn(p, isAbstract, _children.Count > 0 ? S.Virtual : null, allParts))); outBody.AddRange(Parts.WithIndexes() .Where(kvp => kvp.Value.NameId.Name.Name != "Item" + (baseParts.Count + kvp.Key + 1)) .Select(kvp => kvp.Value.GetItemDecl(baseParts.Count + kvp.Key + 1))); outBody.AddRange(_classBody); list.Add(LNode.Call(LNode.List(_classAttrs), CodeSymbols.Class, LNode.List(TypeName, LNode.Call(CodeSymbols.AltList, LNode.List(BaseTypes)), LNode.Call(CodeSymbols.Braces, LNode.List(outBody)).SetStyle(NodeStyle.Statement)))); if (_genericArgs.Count > 0 && Parts.Count > 0) { var argNames = allParts.Select(p => p.NameId); list.Add(LNode.Call(LNode.List().AddRange(_classAttrs).Add(LNode.Id(CodeSymbols.Static)).Add(LNode.Id(CodeSymbols.Partial)), CodeSymbols.Class, LNode.List(_typeNameStem, LNode.Call(CodeSymbols.AltList), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(LNode.List(LNode.Id(CodeSymbols.Public), LNode.Id(CodeSymbols.Static)), CodeSymbols.Fn, LNode.List(TypeNameWithoutAttrs, LNode.Call(CodeSymbols.Of, LNode.List().Add(LNode.Id((Symbol) "New")).AddRange(_genericArgs)).SetStyle(NodeStyle.Operator), LNode.Call(CodeSymbols.AltList, LNode.List(args)), LNode.Call(CodeSymbols.Braces, LNode.List(LNode.Call(CodeSymbols.Return, LNode.List(LNode.Call(CodeSymbols.New, LNode.List(LNode.Call(TypeNameWithoutAttrs, LNode.List(argNames)))))))).SetStyle(NodeStyle.Statement))))).SetStyle(NodeStyle.Statement)))); } foreach (var child in _children) child.GenerateOutput(ref list); }
public void TestSublistProblem() { // This problem affects FVList.PreviousIn(), VList.NextIn(), // AddRange(list, excludeSubList), VList.Enumerator when used with a // range. // Normally this works fine: VList<int> subList = new VList<int>(), list; subList.AddRange(new int[] { 1, 2, 3, 4, 5, 6, 7 }); list = subList; list.Add(8); Assert.That(subList.NextIn(list).Last == 8); // But try it a second time and the problem arises, without some special // code in VListBlock<T>.FindNextBlock() that has been added to // compensate. I call the problem copy-causing-sharing-failure. You see, // right now subList is formed from three blocks: a size-8 block that // contains {7}, a size-4 block {3, 4, 5, 6} and a size-2 block {1, 2}. // But the size-8 block actually has two items {7, 8} and when we // attempt to add 9, a new array must be created. It might waste a lot // of memory to make a new block {9} that links to the size-8 block that // contains {7}, so instead a new size-8 block {7, 9} is created that // links directly to {3, 4, 5, 6}. That way, the block {7, 8} can be // garbage-collected if it is no longer in use. But a side effect is // that subList no longer appears to be a part of list. The fix is to // notice that list (block {7, 9}) and subList (block that contains {7}) // have the same prior list, {3, 4, 5, 6}, and that the remaining // item(s) in subList (just one item, {7}, in this case) are also // present in list. list = subList; list.Add(9); Assert.AreEqual(9, subList.NextIn(list).Last); }
void TestAddRangePair() { VList<int> list = new VList<int>(); VList<int> list2 = new VList<int>(); list2.AddRange(new int[] { 1, 2, 3, 4 }); list.AddRange(list2, list2.WithoutLast(1)); list.AddRange(list2, list2.WithoutLast(2)); list.AddRange(list2, list2.WithoutLast(3)); list.AddRange(list2, list2.WithoutLast(4)); ExpectList(list, 1, 2, 3, 1, 2, 1); AssertThrows<InvalidOperationException>(delegate() { list2.AddRange(list2.WithoutLast(1), list2); }); AssertThrows<InvalidOperationException>(delegate() { list2.AddRange(VList<int>.Empty, list2); }); }
public void TestToArray() { VList<int> list = new VList<int>(); int[] array = list.ToArray(); Assert.AreEqual(array.Length, 0); array = list.Add(1).ToArray(); ExpectList(array.AsListSource(), 1); array = list.Add(2).ToArray(); ExpectList(array.AsListSource(), 1, 2); array = list.Add(3).ToArray(); ExpectList(array.AsListSource(), 1, 2, 3); array = list.AddRange(new int[] { 4, 5, 6, 7, 8 }).ToArray(); ExpectList(array.AsListSource(), 1, 2, 3, 4, 5, 6, 7, 8); }
public void TestEmptyListOperations() { VList<int> a = new VList<int>(); VList<int> b = new VList<int>(); a.AddRange(b); a.InsertRange(0, b); a.RemoveRange(0, 0); Assert.That(!a.Remove(0)); Assert.That(a.IsEmpty); a.Add(1); b.AddRange(a); ExpectList(b, 1); b.RemoveAt(0); Assert.That(b.IsEmpty); b.InsertRange(0, a); ExpectList(b, 1); b.RemoveRange(0, 1); Assert.That(b.IsEmpty); b.Insert(0, a[0]); ExpectList(b, 1); b.Remove(a.Last); Assert.That(b.IsEmpty); AssertThrows<InvalidOperationException>(delegate() { a.NextIn(b); }); }
public void SimpleTests() { // In this simple test, I only add and remove items from the back // of a VList, but forking is also tested. VList<int> list = new VList<int>(); Assert.That(list.IsEmpty); // Adding to VListBlockOfTwo list = new VList<int>(10, 20); ExpectList(list, 10, 20); list = new VList<int>(); list.Add(1); Assert.That(!list.IsEmpty); list.Add(2); ExpectList(list, 1, 2); // A fork in VListBlockOfTwo. Note that list2 will use two VListBlocks // here but list will only use one. VList<int> list2 = list.WithoutLast(1); list2.Add(3); ExpectList(list, 1, 2); ExpectList(list2, 1, 3); // Try doubling list2 list2.AddRange(list2); ExpectList(list2, 1, 3, 1, 3); // list now uses two arrays list.Add(4); ExpectList(list, 1, 2, 4); // Try doubling list using a different overload of AddRange() list.AddRange((IList<int>)list); ExpectList(list, 1, 2, 4, 1, 2, 4); list = list.WithoutLast(3); ExpectList(list, 1, 2, 4); // Remove(), Pop() Assert.AreEqual(3, list2.Pop()); ExpectList(list2, 1, 3, 1); Assert.That(!list2.Remove(0)); Assert.AreEqual(1, list2.Pop()); Assert.That(list2.Remove(3)); ExpectList(list2, 1); Assert.That(list2.Remove(1)); ExpectList(list2); AssertThrows<Exception>(delegate() { list2.Pop(); }); // Add many, SubList(). This will fill 3 arrays (sizes 8, 4, 2) and use // 1 element of a size-16 array. Oh, and test the enumerator. for (int i = 5; i <= 16; i++) list.Add(i); ExpectList(list, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); list2 = list.WithoutLast(6); ExpectListByEnumerator(list2, 1, 2, 4, 5, 6, 7, 8, 9, 10); AssertThrows<IndexOutOfRangeException>(delegate() { int i = list[-1]; }); AssertThrows<IndexOutOfRangeException>(delegate() { int i = list[15]; }); // IndexOf, contains Assert.That(list.Contains(11)); Assert.That(!list2.Contains(11)); Assert.That(list[list.IndexOf(2)] == 2); Assert.That(list[list.IndexOf(1)] == 1); Assert.That(list[list.IndexOf(15)] == 15); Assert.That(list.IndexOf(3) == -1); // PreviousIn(), Last VList<int> list3 = list2; Assert.AreEqual(11, (list3 = list3.NextIn(list)).Last); Assert.AreEqual(12, (list3 = list3.NextIn(list)).Last); Assert.AreEqual(13, (list3 = list3.NextIn(list)).Last); Assert.AreEqual(14, (list3 = list3.NextIn(list)).Last); Assert.AreEqual(15, (list3 = list3.NextIn(list)).Last); Assert.AreEqual(16, (list3 = list3.NextIn(list)).Last); AssertThrows<Exception>(delegate() { list3.NextIn(list); }); // Next Assert.AreEqual(10, (list3 = list3.WithoutLast(6)).Last); Assert.AreEqual(9, (list3 = list3.Tail).Last); Assert.AreEqual(8, (list3 = list3.Tail).Last); Assert.AreEqual(7, (list3 = list3.Tail).Last); Assert.AreEqual(6, (list3 = list3.Tail).Last); Assert.AreEqual(5, (list3 = list3.Tail).Last); Assert.AreEqual(4, (list3 = list3.Tail).Last); Assert.AreEqual(2, (list3 = list3.Tail).Last); Assert.AreEqual(1, (list3 = list3.Tail).Last); Assert.That((list3 = list3.Tail).IsEmpty); // list2 is still the same ExpectList(list2, 1, 2, 4, 5, 6, 7, 8, 9, 10); // ==, !=, Equals(), AddRange(a, b) Assert.That(!list2.Equals("hello")); list3 = list2; Assert.That(list3.Equals(list2)); Assert.That(list3 == list2); // This AddRange forks the list. List2 ends up with block sizes 8 (3 // used), 8 (3 used), 4, 2. list2.AddRange(list2, list2.WithoutLast(3)); ExpectList(list2, 1, 2, 4, 5, 6, 7, 8, 9, 10, 8, 9, 10); Assert.That(list3 != list2); // List3 is a sublist of list, but list2 no longer is Assert.That(list3.NextIn(list).Last == 11); AssertThrows<InvalidOperationException>(delegate() { list2.NextIn(list); }); list2 = list2.WithoutLast(3); Assert.That(list3 == list2); }