.Net Runtime Library for Delphi
Close
Generic Collections

Generic collections were added in the .NET Framework 2.0 and provide collections that are type-safe at compile time. Because of this, generic collections typically offer better performance. Generic collections accept a type parameter when they are constructed and do not require that you cast to and from the Object type when you add or remove items from the collection. In the runtime Library, Generics are prefixed with Generic. For example GenericList interface represent in C# as List Class. Generics allow you to delay the specification of the data type of programming elements in a class or a method, until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type. The following are list of some of the types of the Generic Collections with code examples.

GenericList Collections

This interface represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists. This is the generic equivalent of the ArrayList interface. GenericList accepts null as a valid value for reference types and allows duplicate elements. Elements in this collection can be accessed using an integer index. Indexes in this collection are zero-based. The GenericList is not guaranteed to be sorted. You must sort the GenericList before performing operations (such as BinarySearch) that require the GenericList to be sorted. In deciding whether to use the GenericList or ArrayList collections, both of which have similar functionality, remember that the GenericList class performs better in most cases and is type safe.

Delphi
program GenericListExample; //List<Int32> {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Collections, CNClrLib.Core; var Console: _Console; list: _GenericList; listEnum: _IEnumerator; intArray: _Int32Array; I: Integer; begin Console := CoConsole.CreateInstance; // Create a new Generic List of integer. list := CoGenericList.CreateInstance(TClrAssembly.GetType('System.Int32')); // Add some elements to the List. list.Add(2); list.Add(3); list.Add(5); list.Add(7); // Loop through List with foreach. for I := 0 to list.Count - 1 do Console.WriteLine_8(list[I]); //Change List element at index 0 Console.Write('Changed Generic List at 0 index from {0}', list[0]); list[0] := 300; Console.WriteLine_15(' to {0}', list[0]); // Contains can be used to check if an item exist in the list if list.Contains(300) then Console.WriteLine_14('Value ''300'' in the generic list') else Console.WriteLine_14('Value ''300'' not exist in the generic list'); Console.WriteLine(); // Use while to enumerate the List elements using the IEnumerator Interface; Console.WriteLine_14('Loop through Generic List using IEnumerator Interface'); listEnum := list.AsIEnumerable.GetEnumerator; while listEnum.MoveNext do Console.WriteLine_15('{0}', listEnum.Current); // Use the Remove method to remove an element. Console.WriteLine; Console.WriteLine_14('Remove(300)'); list.Remove(300); if not list.Contains(300) then Console.WriteLine_14('Value ''300'' is not found.'); end. (* This code example produces the following output: 2 3 5 7 Changed Generic List at 0 index from 2 to 300 Value '300' in the generic list Loop through Generic List using IEnumerator Interface 300 3 5 7 Remove(300) Value '300' is not found. *)



































































 

 

GenericDictionary Collections

This interface represents a collection of keys and values. It provides a mapping from a set of keys to a set of values. Each addition to the dictionary consists of a value and its associated key. Retrieving a value by using its key is very fast. The following code example creates an empty GenericDictionary of strings with string keys and uses the Add method to add some elements. The example demonstrates that the Add method throws an ArgumentException when attempting to add a duplicate key. The example uses the Item property to retrieve values, demonstrating that a KeyNotFoundException is thrown when a requested key is not present, and showing that the value associated with a key can be replaced. The example shows how to use the TryGetValue method as a more efficient way to retrieve values if a program often must try key values that are not in the dictionary, and it shows how to use the ContainsKey method to test whether a key exists before calling the Add method. The example shows how to enumerate the keys and values in the dictionary and how to enumerate the keys and values alone using the Keys property and the Values property. Finally, the example demonstrates the Remove method.

Delphi
program GenericDictionaryExample; //Dictionary<String, String> {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Collections, CNClrLib.Core, System.Win.ComObj; var Console: _Console; openWith: _GenericDictionary; Values: _IEnumerator; Keys: _IEnumerator; ClrEx: EClrException; I: Integer; s: string; value: OleVariant; begin Console := CoConsole.CreateInstance; // Create a new dictionary of strings, with string keys. // openWith := CoGenericDictionary.CreateInstance(TClrAssembly.GetType('System.String'), TClrAssembly.GetType('System.String')); // Add some elements to the dictionary. There are no // duplicate keys, but some of the values are duplicates. openWith.Add('txt', 'notepad.exe'); openWith.Add('bmp', 'paint.exe'); openWith.Add('dib', 'paint.exe'); openWith.Add('rtf', 'wordpad.exe'); // The Add method throws an exception if the new key is // already in the dictionary. try openWith.Add('txt', 'winword.exe'); except on Eole: EOleSysError do begin ClrEx := EClrException.GetLastClrException; try if ClrEx.IsTypeOf('System.ArgumentException') then Console.WriteLine_14('An element with Key = ''txt'' already exists.'); finally ClrEx.Free; end; end; end; // The Item property is the default property, so you // can omit its name when accessing elements. Console.WriteLine_15('For key = ''rtf'', value = {0}.', openWith['rtf']); // The default Item property can be used to change the value // associated with a key. openWith['rtf'] := 'winword.exe'; Console.WriteLine_15('For key = ''rtf'', value = {0}.', openWith['rtf']); // If a key does not exist, setting the default Item property // for that key adds a new key/value pair. openWith['doc'] := 'winword.exe'; // The indexer throws an exception if the requested key is // not in the dictionary. try Console.WriteLine_15('For key = ''tif'', value = {0}.', openWith['tif']); except on Eole: EOleSysError do begin ClrEx := EClrException.GetLastClrException; try if ClrEx.IsTypeOf('System.Collections.Generic.KeyNotFoundException') then Console.WriteLine_14('Key = ''tif'' is not found.'); finally ClrEx.Free; end; end; end; // When a program often has to try keys that turn out not to // be in the dictionary, TryGetValue can be a more efficient // way to retrieve values. value := ''; if openWith.TryGetValue('tif', value) then Console.WriteLine_15('For key = ''tif'', value = {0}.', value) else Console.WriteLine_14('Key = ''tif'' is not found.'); // ContainsKey can be used to test keys before inserting // them. if not openWith.ContainsKey('ht') then begin openWith.Add('ht', 'hypertrm.exe'); Console.WriteLine_15('Value added for key = ''ht'': {0}', openWith['ht']); end; // Use while to enumerate the dictionary elements; // Use the Keys property (ICollection) inherited class called IEnumerable in the // form of a method in the ICollection interface called AsIEnumerable and get the // GetEnumerator for the keys. Enumerate through the key values and display both the // key and value of the dictionary per iteration. Console.WriteLine(); Keys := openWith.Keys.AsIEnumerable.GetEnumerator; while Keys.MoveNext do Console.WriteLine_17('Key = {0}, Value = {1}', Keys.Current, openWith.Item[Keys.Current]); // To get the values alone, use the Values property. Values := openWith.Values.AsIEnumerable.GetEnumerator; // The elements of the ValueCollection are strongly typed // with the type that was specified for hash table values. Console.WriteLine(); while Values.MoveNext do Console.WriteLine_15('Value = {0}', Values.Current); // Reset Keys Collection Enumerator values to point to the first data in the list // because it has been used above. Keys.Reset; // The elements of the KeyCollection are strongly typed // with the type that was specified for hash table keys. Console.WriteLine(); while Keys.MoveNext do Console.WriteLine_15('Key = {0}', Keys.Current); // Use the Remove method to remove a key/value pair. Console.WriteLine; Console.WriteLine_14('Remove(''doc'')'); openWith.Remove('doc'); if not openWith.ContainsKey('doc') then Console.WriteLine_14('Key ''doc'' is not found.'); end. (* This code example produces the following output: An element with Key = 'txt' already exists. For key = 'rtf', value = wordpad.exe. For key = 'rtf', value = winword.exe. Key = 'tif' is not found. Key = 'tif' is not found. Value added for key = 'ht': hypertrm.exe Key = txt, Value = notepad.exe Key = bmp, Value = paint.exe Key = dib, Value = paint.exe Key = rtf, Value = winword.exe Key = doc, Value = winword.exe Key = ht, Value = hypertrm.exe Value = notepad.exe Value = paint.exe Value = paint.exe Value = winword.exe Value = winword.exe Value = hypertrm.exe Key = txt Key = bmp Key = dib Key = rtf Key = doc Key = ht Remove('doc') Key 'doc' is not found. *)


























































































































































 

GenericHashSet Collections

This interface represents a set of values. It provides high-performance set operations. A set is a collection that contains no duplicate elements, and whose elements are in no particular order. The following example demonstrates how to merge two disparate sets. This example creates two GenericHashSet objects, and populates them with even and odd numbers, respectively. A third GenericHashSet object is created from the set that contains the even numbers. The example then calls the UnionWith method, which adds the odd number set to the third set.

Delphi
program GenericHashSetExample; //HashSet<Int32> {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Collections, CNClrLib.Core; var Console: _Console; evenNumbers: _GenericHashSet; oddNumbers: _GenericHashSet; numbers: _GenericHashSet; intType: _Type; i: Integer; procedure DisplaySet(Aset: _GenericHashSet); var ASetEnum: _IEnumerator; begin Console.Write_22('{'); ASetEnum := Aset.AsIEnumerable.GetEnumerator; while ASetEnum.MoveNext do Console.Write(' {0}', ASetEnum.Current); Console.WriteLine_14(' }'); end; begin Console := CoConsole.CreateInstance; intType := TClrAssembly.GetType('System.Int32'); evenNumbers := CoGenericHashSet.CreateInstance(intType); oddNumbers := CoGenericHashSet.CreateInstance(intType); for i := 0 to 4 do begin // Populate numbers with just even numbers. evenNumbers.Add(i * 2); // Populate oddNumbers with just odd numbers. oddNumbers.Add((i * 2) + 1); end; Console.Write('evenNumbers contains {0} elements: ', evenNumbers.Count); DisplaySet(evenNumbers); Console.Write('oddNumbers contains {0} elements: ', oddNumbers.Count); DisplaySet(oddNumbers); // Create a new HashSet populated with even numbers. numbers := CoGenericHashSet.CreateInstance(evenNumbers); Console.WriteLine_14('numbers UnionWith oddNumbers...'); numbers.UnionWith(oddNumbers.AsGenericIEnumerable); Console.Write('numbers contains {0} elements: ', numbers.Count); DisplaySet(numbers); end. (* This code example produces the following output: evenNumbers contains 5 elements: { 0 2 4 6 8 } oddNumbers contains 5 elements: { 1 3 5 7 9 } numbers UnionWith oddNumbers... numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 } *)



























































 

GenericLinkedList Collections

This interface represent a doubly linked list. It is a general-purpose linked list. GenericLinkedList provides separate nodes of type GenericLinkedListNode, so insertion and removal are O(1) operations. You can remove nodes and reinsert them, either in the same list or in another list, which results in no additional objects allocated on the heap. The following code example demonstrates many features of the GenericLinkedList interface.

Delphi
program ExampleApp; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Collections, CNClrLib.Core, System.Variants, System.Win.ComObj; var Console: _Console; words: _StringArray; sArray: _GenericArray; stringType: _Type; sentence: _GenericLinkedList; mark1: _GenericLinkedListNode; mark2: _GenericLinkedListNode; current: _GenericLinkedListNode; icoll: _GenericICollection; ClrEx: EClrException; s: string; I: Integer; procedure Display(words: _GenericLinkedList; test: string); var AEnumerator: _IEnumerator; begin Console.WriteLine_14(test); AEnumerator := words.AsIEnumerable.GetEnumerator; while AEnumerator.MoveNext do Console.Write_22(AEnumerator.Current + ' '); Console.WriteLine; Console.WriteLine; end; procedure IndicateNode(node: _GenericLinkedListNode; test: string); var result: _StringBuilder; nodeP: _GenericLinkedListNode; begin Console.WriteLine_14(test); if node.List = nil then begin Console.WriteLine_15('Node ''{0}'' is not in the list.', node.Value); Console.WriteLine; exit; end; result := CoStringBuilder.CreateInstance('(' + VarToStr(node.Value) + ')'); nodeP := node.Previous; while nodeP <> nil do begin result.Insert_17(0, nodeP.Value + ' '); nodeP := nodeP.Previous; end; node := node.Next; while nodeP <> nil do begin result.Append_17(' ' + node.Value); node := node.Next; end; Console.WriteLine_12(result); Console.WriteLine; end; begin Console := CoConsole.CreateInstance; // Create the link list. words := TClrArrayHelper.ToStringArray(['the', 'fox', 'jumped', 'over', 'the', 'dog']); stringType := TClrAssembly.GetType('System.String'); //Convert the StringArray to sentence := CoGenericLinkedList.CreateInstance(words.AsArray.AsGenericIEnumerable); Display(sentence, 'The linked list values:'); Console.WriteLine_15('sentence.Contains(''jumped'') = {0}', sentence.Contains('jumped')); // Add the word 'today' to the beginning of the linked list. sentence.AddFirst('today'); Display(sentence, 'Test 1: Add ''today'' to beginning of the list:'); // Move the first node to be the last node. mark1 := sentence.First; sentence.RemoveFirst; sentence.AddLast_2(mark1); Display(sentence, 'Test 2: Move first node to be last node:'); // Change the last node be 'yesterday'. sentence.RemoveLast; sentence.AddLast('yesterday'); Display(sentence, 'Test 3: Change the last node to ''yesterday'':'); // Move the last node to be the first node. mark1 := sentence.Last; sentence.RemoveLast; sentence.AddFirst_2(mark1); Display(sentence, 'Test 4: Move last node to be first node:'); // Indicate, by using parentheisis, the last occurence of 'the'. sentence.RemoveFirst(); current := sentence.FindLast('the'); IndicateNode(current, 'Test 5: Indicate last occurence of ''the'':'); // Add 'lazy' and 'old' after 'the' (the LinkedListNode named current). sentence.AddAfter_1(current, 'old'); sentence.AddAfter_1(current, 'lazy'); IndicateNode(current, 'Test 6: Add ''lazy'' and ''old'' after ''the'':'); // Indicate 'fox' node. current := sentence.Find('fox'); IndicateNode(current, 'Test 7: Indicate the ''fox'' node:'); // Add 'quick' and 'brown' before 'fox': sentence.AddBefore(current, 'quick'); sentence.AddBefore(current, 'brown'); IndicateNode(current, 'Test 8: Add ''quick'' and ''brown'' before ''fox'':'); // Keep a reference to the current node, 'fox', // and to the previous node in the list. Indicate the 'dog' node. mark1 := current; mark2 := current.Previous; current := sentence.Find('dog'); IndicateNode(current, 'Test 9: Indicate the ''dog'' node:'); // The AddBefore method throws an InvalidOperationException // if you try to add a node that already belongs to a list. Console.WriteLine_14('Test 10: Throw exception by adding node (fox) already in the list:'); try sentence.AddBefore(current, mark1); except on Ex: EOleSysError do begin ClrEx := EClrException.GetLastClrException; try if ClrEx.IsTypeOf('System.InvalidOperationException') then Console.WriteLine_15('Exception message: {0}', Ex.Message); finally ClrEx.Free; end; end; end; Console.WriteLine; // Remove the node referred to by mark1, and then add it // before the node referred to by current. // Indicate the node referred to by current. sentence.Remove_2(mark1); sentence.AddBefore_2(current, mark1); IndicateNode(current, 'Test 11: Move a referenced node (fox) before the current node (dog):'); // Remove the node referred to by current. sentence.Remove_2(current); IndicateNode(current, 'Test 12: Remove current node (dog) and attempt to indicate it:'); // Add the node after the node referred to by mark2. sentence.AddAfter(mark2, current); IndicateNode(current, 'Test 13: Add node removed in test 11 after a referenced node (brown):'); // The Remove method finds and removes the // first node that that has the specified value. sentence.Remove('old'); Display(sentence, 'Test 14: Remove node that has the value ''old'':'); Console.WriteLine_14('Test 15: Copy the list to an array:'); // Create an array with the same number of // elements as the inked list. sArray := CoGenericArray.CreateInstance(stringType, sentence.Count); sentence.CopyTo(sArray, 0); for I := 0 to sArray.Length - 1 do Console.WriteLine_12(sArray.GetValue(I)); // Release all the nodes. sentence.Clear(); Console.WriteLine(); Console.WriteLine_15('Test 16: Clear linked list. Contains ''jumped'' = {0}', sentence.Contains('jumped')); Console.ReadLine(); end. (* This code example produces the following output: The linked list values: the fox jumped over the dog sentence.Contains('jumped') = True Test 1: Add 'today' to beginning of the list: today the fox jumped over the dog Test 2: Move first node to be last node: the fox jumped over the dog today Test 3: Change the last node to 'yesterday': the fox jumped over the dog yesterday Test 4: Move last node to be first node: yesterday the fox jumped over the dog Test 5: Indicate last occurence of 'the': the fox jumped over (the) Test 6: Add 'lazy' and 'old' after 'the': the fox jumped over (the) Test 7: Indicate the 'fox' node: the (fox) Test 8: Add 'quick' and 'brown' before 'fox': the quick brown (fox) Test 9: Indicate the 'dog' node: the quick brown fox jumped over the lazy old (dog) Test 10: Throw exception by adding node (fox) already in the list: Test 11: Move a referenced node (fox) before the current node (dog): the quick brown jumped over the lazy old fox (dog) Test 12: Remove current node (dog) and attempt to indicate it: Node 'dog' is not in the list. Test 13: Add node removed in test 11 after a referenced node (brown): the quick brown (dog) Test 14: Remove node that has the value 'old': the quick brown dog jumped over the lazy fox Test 15: Copy the list to an array: the quick brown dog jumped over the lazy fox Test 16: Clear linked list. Contains 'jumped' = False *)











































































































































































































































 

GenericStack Collections

This interface represents a variable size last-in-first-out (LIFO) collection of instances of the same specified type. Stacks are useful when you need temporary storage for information; that is, when you might want to discard an element after retrieving its value. Use GenericQueue if you need to access the information in the same order that it is stored in the collection. Use GenericStack if you need to access the information in reverse order. GenericStack accepts null as a valid value for reference types and allows duplicate elements. The following code example demonstrates several methods of the GenericStack interface. The code example creates a stack of strings with default capacity and uses the Push method to push five strings onto the stack. The elements of the stack are enumerated, which does not change the state of the stack. The Pop method is used to pop the first string off the stack. The Peek method is used to look at the next item on the stack, and then the Pop method is used to pop it off.

Delphi
program GenericStackExample; // Stack<String> {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, CNClrLib.Host, CNClrLib.Collections, CNClrLib.Core; var Console: _Console; numbers: _GenericStack; stack2: _GenericStack; stackEnum: _IEnumerator; stack2Enum: _IEnumerator; stringType: _Type; begin Console := CoConsole.CreateInstance; stringType := TClrAssembly.GetType('System.String'); numbers := CoGenericStack.CreateInstance(stringType); numbers.Push('one'); numbers.Push('two'); numbers.Push('three'); numbers.Push('four'); numbers.Push('five'); // A stack can be enumerated without disturbing its contents. stackEnum := numbers.AsIEnumerable.GetEnumerator; while stackEnum.MoveNext do Console.WriteLine_12(stackEnum.Current); Console.WriteLine; Console.WriteLine_15('Popping ''{0}''', numbers.Pop()); Console.WriteLine_15('Peek at next item to destack: {0}', numbers.Peek()); Console.WriteLine_15('Popping ''{0}''', numbers.Pop()); // Create a copy of the stack, using the AsGenericIEnumerable method and the // constructor that accepts a Generic Object which can be GenericIEnumerable, // GenericStack etc stack2 := CoGenericStack.CreateInstance(numbers.AsGenericIEnumerable); Console.WriteLine_14('Contents of the first copy:'); stack2Enum := stack2.AsIEnumerable.GetEnumerator; while stack2Enum.MoveNext do Console.WriteLine_12(stack2Enum.Current); Console.WriteLine; Console.WriteLine_15('stack2.Contains(''four'') = {0}', stack2.Contains('four')); Console.WriteLine_14('stack2.Clear()'); stack2.Clear(); Console.WriteLine_15('stack2.Count = {0}', stack2.Count); end. (* This code example produces the following output: five four three two one Popping 'five' Peek at next item to destack: four Popping 'four' Contents of the first copy: one two three stack2.Contains('four') = False stack2.Clear() stack2.Count = 0 *)