{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Session 5: Collections, Generics, and a peek into LINQ\n",
"\n",
"We've worked with loops, conditions, methods, and our own types in C# but what about collections of objects? A group of Person objects or Products that are added to a shopping cart, how do we handle those? \n",
"\n",
"## Collections\n",
"\n",
"There are a number of different collection objects that you can use that implement the same basic interactions.\n",
"\n",
"### Array\n",
"\n",
"[Arrays](https://docs.microsoft.com/dotnet/csharp/programming-guide/arrays?WT.mc_id=visualstudio-twitch-jefritz) are reference types and the simplest of the collection types, and can be declared with one to many dimensions and can also be declared jagged. Simplify declared with a type and square brackets `[ ]` defining the size of the array, initialized with a `new` statement and curly braces `{ }` optionally containing the initial values of the array."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\r\n",
"
\r\n",
"
\r\n",
" The below script needs to be able to find the current output cell; this is an easy method to get it.\r\n",
"
\r\n",
" \r\n",
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"Array is created: True"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"Array is null: False"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"Array Size: 3"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"Array Size: 3"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"int[] numbers;\n",
"\n",
"// Numbers doesn't contain anything, as it wasn't assigned yet\n",
"display(\"Array is created: \" + (numbers == null).ToString());\n",
"\n",
"// Create an array by using square brackets containing a size \n",
"numbers = new int[3];\n",
"display(\"Array is null: \" + (numbers == null).ToString());\n",
"\n",
"// The read-only Length property shows the number of elements in the array \n",
"display(\"Array Size: \" + numbers.Length);\n",
"\n",
"// Declare the array with initial values\n",
"var fullArrayOfNumbers = new int[3] {1, 2, 3};\n",
"display(\"Array Size: \" + fullArrayOfNumbers.Length);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can then interact with the values of the array using numeric a numeric indexer starting with a base value of 0"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Item[0]: 1"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"Item[0]: 5"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"display(\"Item[0]: \" + fullArrayOfNumbers[0]);\n",
"\n",
"// You can set values on the array using the equals assignment operator\n",
"fullArrayOfNumbers[0] = 5;\n",
"display(\"Item[0]: \" + fullArrayOfNumbers[0]);"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"ename": "Unhandled exception",
"evalue": "System.IndexOutOfRangeException: Index was outside the bounds of the array.\r\n at Submission#6.<>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)",
"output_type": "error",
"traceback": [
"System.IndexOutOfRangeException: Index was outside the bounds of the array.\r\n at Submission#6.<>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)",
" at Submission#6.<>d__0.MoveNext()",
"--- End of stack trace from previous location where exception was thrown ---",
" at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)"
]
}
],
"source": [
"// You cannot interact with array values outside the size of the array\n",
"// display(fullArrayOfNumbers[5]);\n",
"fullArrayOfNumbers[5] = 100;"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"6"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"2"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"// You can work with multi-dimensional arrays as well\n",
"var matrix = new int[3,2] { {1,2}, {3,4}, {5,6} };\n",
"display(matrix.Length);\n",
"\n",
"// Access elements of the multi-dimensional using a comma between index values\n",
"matrix[0,1]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The challenge with arrays is that you cannot easily add or remove objects from the array without going through a complex bit of resizing using the `Array.Resize` method."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
index
value
0
1
1
2
2
3
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"var myNumbers = new int[] {1,2,3};\n",
"display(myNumbers);\n",
"\n",
"// This doesn't work\n",
"//myNumbers.Add(4);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can change the size of a one-dimensional array using the [Array.Resize](https://docs.microsoft.com/dotnet/api/system.array.resize?view=netcore-3.1&WT.mc_id=visualstudio-twitch-jefritz) method. This method does not _just_ resize the array, but rather creates a new array of the desired size and copies the values into that new array. "
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"Warning: If you have a reference to the array somewhere else in your code, that reference will NOT be resized as well. Arrays are reference types, and references to the prior object are persisted.\n",
"
"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"// Let's see what referenceToMyNumbers contains:\n",
"referenceToMyNumbers"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`referenceToMyNumbers` contains the contents of `myNumbers` before the resize because it was NOT effected by the `Array.Resize` operation which makes a copy of the array.\n",
"\n",
"You can remove elements from the array by resizing to a smaller number of elements. This will eliminate contents from the end of the array."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
index
value
0
1
1
2
2
3
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"// Remove is similar, and eliminates elements from the end of the array\n",
"Array.Resize(ref myNumbers, 3);\n",
"display(myNumbers);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Arrays are enumerable and implement the `IEnumerable` interface, meaning you can iterate over the contents of a collection with a loop and interact with them:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"1"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"2"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"3"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"// i in this case returns the element in the collection, not the index\n",
"\n",
"foreach (var i in myNumbers) {\n",
" display(i);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Array.Fill"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
index
value
0
1
1
1
2
1
"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"var myOneArray = new int[3];\n",
"Array.Fill(myOneArray, 1);\n",
"\n",
"myOneArray"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Hashtable and SortedList\n",
"\n",
"A [Hashtable](https://docs.microsoft.com/dotnet/api/system.collections.hashtable?view=netcore-3.1?WT.mc_id=visualstudio-twitch-jefritz) and [SortedList](https://docs.microsoft.com/en-us/dotnet/system.collections.sortedlist?view=netcore-3.1&WT.mc_id=visualstudio-twitch-jefritz) are collections of key/value pairs that contain no duplicate keys. The `Hashtable` is sorted based on the hash hash of the keys and a `SortedList` is sorted based on the key value"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
key
value
jpg
Jpeg Compressed Images
mp3
Compressed Music
txt
Plain text
"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"//var fileExt = new Hashtable();\n",
"var fileExt = new SortedList();\n",
"fileExt.Add(\"txt\", \"Plain text\");\n",
"fileExt.Add(\"mp3\", \"Compressed Music\");\n",
"fileExt.Add(\"jpg\", \"Jpeg Compressed Images\");\n",
"\n",
"fileExt"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"// No duplicates are allowed\n",
"//fileExt.Add(\"mp3\", \"Sound effects\");"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Compressed Music"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fileExt[\"mp3\"]"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"jpg"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"mp3"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"txt"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"foreach (var kv in fileExt) \n",
"{\n",
" display(((DictionaryEntry)kv).Key);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Queue\n",
"\n",
"A [Queue](https://docs.microsoft.com/dotnet/api/system.collections.queue?view=netcore-3.1&WT.mc_id=visualstudio-twitch-jefritz) is a collection of objects stored and accessed in a first-in / first-out manner. `Enqueue` to add elements to the `Queue` and `Dequeue` to remove elements from the `Queue`. You can also `Peek` to inspect the oldest element in the `Queue`"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
index
value
0
First
1
Second
2
Third
"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"var myQueue = new Queue();\n",
"myQueue.Enqueue(\"First\");\n",
"myQueue.Enqueue(\"Second\");\n",
"\n",
"myQueue.Enqueue(\"Third\");\n",
"\n",
"myQueue"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"3"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"// Use Count to check the size of the queue\n",
"myQueue.Count"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"First"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"// Use Peek to inspect the next value off of the queue\n",
"display(myQueue.Peek());"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"First"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"var z = myQueue.Dequeue();\n",
"z"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Stack\n",
"\n",
"A [Stack](https://docs.microsoft.com/dotnet/api/system.collections.stack?view=netcore-3.1&WT.mc_id=visualstudio-twitch-jefritz) is a collection that is accessed in Last-in/First-out manner using the `Push` and `Pop` methods to add and remove items, with the `Peek` method available to examine the next item to be removed from the `Stack`. I think of a `Stack` like a deck of cards: the last card that is placed on the top of the deck is the first to be dealt to a player."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
index
value
0
9-c
1
9-s
2
9-h
3
A-s
4
A-d
"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"var myHand = new Stack();\n",
"myHand.Push(\"A-d\");\n",
"myHand.Push(\"A-s\");\n",
"myHand.Push(\"9-h\");\n",
"myHand.Push(\"9-s\");\n",
"myHand.Push(\"9-c\");\n",
"\n",
"myHand"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"9-c"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"var myCard = myHand.Peek();\n",
"myCard // The 9-Clubs is returned first because it was Pushed onto the Stack LAST"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"9-c"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"9-s"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"9-h"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"A-s"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"A-d"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"9-c"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"9-c"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"foreach (var item in myHand) \n",
"{\n",
" display(item);\n",
"}\n",
"\n",
"display(myHand.Peek());\n",
"var thisCard = myHand.Pop();\n",
"thisCard"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We've looked at these basic interactions with collection types and the keys or values stored don't have a specific type associated. You can get into some hairy situations dealing with type conversions if you mix and match types for keys or values."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"// take a card off the deck\n",
"var myCard = deckOfCards.Pop();\n",
"myCard"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generics \n",
"\n",
"[Generics](https://docs.microsoft.com/dotnet/csharp/programming-guide/generics?WT.mc_id=visualstudio-twitch-jefritz) are a way for you to force the type of a parameter from within client code. You declare the type generically using the convention `` with a class name or a method name and this allows that type to be passed around and enforced on those methods or properties in a class.\n",
"\n",
"Most developers are familiar with using [Generic Collections](https://docs.microsoft.com/dotnet/standard/generics/collections?WT.mc_id=visualstudio-twitch-jefritz), which enforce the type of the objects in the collection. You'll find a `Queue` and a `Stack` available in the `System.Collections.Generic` namespace that mirror the versions we used above, as well as a few others list `List` and `Dictionary`.\n",
"\n",
"Let's take a look at some examples\n",
"\n",
"### List<T>\n",
"\n",
"The [List<T>](https://docs.microsoft.com/dotnet/api/system.collections.generic.list-1?view=netcore-3.1&WT.mc_id=visualstudio-twitch-jefritz) is the most flexible of the generic collections, allowing you to add, remove, and access objects of the specified type. Let's take a look at that deck of cards sample again:"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"System.Collections.Generic.List<Submission#26+Card>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"
index
Rank
Suit
0
A
d
1
J
d
2
9
c
3
8
s
"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"// Declare the list with the specified type inside angle-brackets\n",
"var listOfCards = new List();\n",
"listOfCards.Add(new Card(\"A-d\"));\n",
"listOfCards.Add(new Card(\"J-d\"));\n",
"listOfCards.Add(new Card(\"9-c\"));\n",
"listOfCards.Add(new Card(\"8-s\"));\n",
"\n",
"display(listOfCards.GetType());\n",
"listOfCards"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can randomly access an element anywhere in the `List`, similar to choosing a card from the middle of the deck:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
Rank
Suit
9
c
"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"listOfCards[2]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similary, we can also `Insert` to add a card into the middle of the deck at a specific index. Perhaps I have the three of hearts and want to insert it as the third card in the collection:"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
index
Rank
Suit
0
A
d
1
J
d
2
3
h
3
9
c
4
8
s
"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"var ThreeHearts = new Card(\"3-h\");\n",
"listOfCards.Insert(2, ThreeHearts);\n",
"listOfCards"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I can ask the list where the three of hearts is located by using the `IndexOf` method to locate it in the deck. I bet that sleight of hand magician could use this technique to find the three of hearts in a deck of cards. Its not magic, just C#:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"2"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"listOfCards.IndexOf(ThreeHearts)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can also ask the `List` what the card is at a specific index by using the `ElementAt` method"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
Rank
Suit
J
d
"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"listOfCards.ElementAt(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This list is generically typed to a `Card` and we read the type `List` in English as \"List **of type** Card\". Thanks to this typing, we cannot add anything that isn't a card to the `listOfCards`:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [],
"source": [
"//listOfCards.Add(\"Joker\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dictionary<TKey,TValue>\n",
"\n",
"In some classes, you may have multiple type-arguments like the Dictionary class. In [Dictionary<TKey,TValue>](https://docs.microsoft.com/dotnet/api/system.collections.generic.dictionary-2?view=netcore-3.1&WT.mc_id=visualstudio-twitch-jefritz) there are type arguments for the key and the value stored."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"var set = new HashSet();\n",
"set.Add(new Card(\"J-c\"));\n",
"set.Add(new Card(\"A-c\"));\n",
"set.Add(new Card(\"9-d\"));\n",
"\n",
"var threeHearts = new Card(\"3-h\");\n",
"set.Add(threeHearts);\n",
"\n",
"display(set);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we attempt to add the 3 of Hearts a second time, it doesn't actually add another card to the Hashset because the 3 of Hearts is already present:"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
index
Rank
Suit
0
J
c
1
A
c
2
9
d
3
3
h
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"set.Add(threeHearts);\n",
"display(set);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Creating Generic Classes\n",
"\n",
"Ok, generics are cool... but how do you create your own classes or methods to work with them? Perhaps we have our own custom collection object that randomly inserts objects into the collection and we want to work with the objects generically.\n",
"\n",
"The official documentation on [Generics](https://docs.microsoft.com/dotnet/standard/generics/?WT.mc_id=visualstudio-twitch-jefritz) has more details about how to interact with the managed types of the class.\n",
"\n",
"Get started by declaring your class and methods using the `` notation to indicate that this is a generic type-parameter where the characters inside the angle-brackets are identical for all methods with the same characters."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
_Inner
[ 2, 4, 1, 3, 5 ]
"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"class FritzSet \n",
"{\n",
" // This normally wouldn't be public scoped, but making it public for the notebook visualizer\n",
" // The for this List is the same type as the that this class will be created with\n",
" public List _Inner = new List();\n",
" \n",
" // The newItem parameter will be of type T, matching the same type assigned when this class is created\n",
" public void Add(T newItem) \n",
" {\n",
" var insertAt = _Inner.Count == 0 ? 0 : new Random().Next(0,_Inner.Count+1);\n",
" _Inner.Insert(insertAt, newItem);\n",
" }\n",
" \n",
"}\n",
"\n",
"// Let's insert some numbers into a FritzSet of type 'int'\n",
"var set = new FritzSet();\n",
"set.Add(1);\n",
"set.Add(2);\n",
"set.Add(3);\n",
"set.Add(4);\n",
"set.Add(5);\n",
"set"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
_Inner
[ { Submission#26+Card: Rank: 9, Suit: d }, { Submission#26+Card: Rank: A, Suit: d }, { Submission#26+Card: Rank: J, Suit: h }, { Submission#26+Card: Rank: 2, Suit: s }, { Submission#26+Card: Rank: 3, Suit: c } ]
"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"// We can also create a FritzSet that holds a bunch of Card objects\n",
"var deck = new FritzSet();\n",
"deck.Add(new Card(\"A-d\"));\n",
"deck.Add(new Card(\"9-d\"));\n",
"deck.Add(new Card(\"J-h\"));\n",
"deck.Add(new Card(\"3-c\"));\n",
"deck.Add(new Card(\"2-s\"));\n",
"\n",
"deck"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
_Inner
[ { Submission#26+Card: Rank: J, Suit: h }, Joker ]
"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"// you can also force a generic type of 'object' and lose that compile-time type checking\n",
"var objSet = new FritzSet