{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "It's time for our code to make some decisions for itself. Every programming language has some concept of conditional execution of code. These features are available in C# with the keywords:\n", "\n", "- `if`\n", "- `else`\n", "- `else if`\n", "- `switch` or `case`\n", "\n", "## Conditional Statements\n", "\n", "There are two statement-level conditional interactions in C#: `if` and `switch...case` statements. `If` statements can be combined with any number of `else if` statements and a single `else` statement to route code flow and interactions across branches of code. ([Link to official docs](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/if-else?WT.mc_id=visualstudio-twitch-jefritz))\n", "\n", "We're going to write some code that you might have in a card game like [Go Fish](https://en.wikipedia.org/wiki/Go_Fish). Go Fish is a card game where you collect cards from other players by guessing what they have in their hand. When you guess correctly, you receive the other players cards that you requested.\n", "\n", "We'll start with an `if` statement to guess if you have any 5s in your hand. You can re-run this code by clicking on the code and clicking the 'Play' button above or using the `Ctrl+Enter` hotkey" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️\";\n", "display(\"My hand: \" + myHand);\n", "\n", "// We can use the Contains method to test if a string contains a value:\n", "display(myHand.Contains(\"5\"));\n", "\n", "// Another player is asking us if we have any 5's\n", "display(\"Do you have any 5's?\");\n", "if ( myHand.Contains(\"5\") ) {\n", " \n", " // Do this thing when seconds are even\n", " display(\"Give me your 5!\");\n", " \n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `if` statement starts with the if keyword and continues with a test in parenthesis. Next, the code to be executed if the test evaluates to true is contained within curly braces `{ }`. The use of braces is optional, as long as the code to be executed is a single line." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️\";\n", "display(\"My hand: \" + myHand);\n", "\n", "// Another player is asking us if we have any 5's\n", "display(\"Do you have any 5's?\");\n", "if ( myHand.Contains(\"5\") )\n", " display(\"Give me your 5!\");\n", "\n", " display(\"This will always execute, even though it LOOKS LIKE its in the if statement\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will always execute, even though it LOOKS LIKE its in the if statement. I recommend you always use braces because it makes it clearer what code is being conditionally executed\n", "\n", "Great!, when the condition is met we can execute some code. What if we need some more complex branching and potentially apply secondary tests and code if those tests return false? We can start using the `else` and `else if` syntax to add these additional branches of code to be executed.\n", "\n", "Let's talk through what happens if we have a 6 or if we have too many cards and what the conversation might look like." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "display(\"My hand: \" + myHand);\n", "\n", "display(\"Do you have any 6's?\");\n", "if (myHand.Contains(\"6\")) {\n", " display(\"Give me your 6!\");\n", "} else if (myHand.Length > 14) {\n", " display(\"You have too many cards in hand!\");\n", "} else {\n", " display(\"Go fish!\");\n", "}\n", "\n", "if (myHand.Contains(\"6\")) display(\"Give me your 6!\");\n", "else if (myHand.Length > 14) display(\"You have too many cards in hand!\");\n", "else display(\"Go fish!\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Testing for one condition is fine... but what if we have a compound scenario where we need to test for multiple factors before we determine which branch to take?\n", "\n", "You can chain together conditional tests using the logical OR `|` and the logical AND `&` operators." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "var cardsLeftInPool = true;\n", "display(\"My hand: \" + myHand);\n", "\n", "display(\"Do you have any 6's?\");\n", "if (!myHand.Contains(\"6\") & cardsLeftInPool) {\n", " display(\"Go Fish! Draw a card from the pool\");\n", " myHand = myHand + \"2♣️\";\n", "} else if (myHand.Contains(\"6\")) {\n", " display(\"Give me your 6\");\n", "} else {\n", " display(\"You don't have a 6 and there's no cards to draw\");\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is another scenario that you will see many developers use to prioritize the compound boolean test inside an if statement, and that is using the 'short circuit' operators `&&` and `||`. They're referred to as a 'short circuit' operators because they evaluate the first condition on the left and if necessary evaluate the condition on the right side of the operator.\n", "\n", "The `&&` operator is called the **Conditional Logical AND** operator or referred to as **AndAlso** in Visual Basic. This operator behaves as follows:\n", "\n", "1. Evaluate the left-side of the operator\n", "2. IF the left-side evaluates to false, return false and stop processing\n", "3. ELSE return the result of evaluating the right-side of the operator\n", "\n", "Let's look at an example where we'll check if I have a 6 in my hand. If I don't have a 6 and there are still cards in the pool, we'll draw a card." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "display(\"My hand: \" + myHand);\n", "\n", "bool CardsLeftInPool() {\n", " display(\"CardsLeftInPool was called\");\n", " return true;\n", "}\n", "\n", "display(\"Do you have a 6?\");\n", "if (!myHand.Contains(\"6\") && CardsLeftInPool()) {\n", " display(\"Go Fish! Draw a card from the pool\");\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this scenario, if we don't have a 6 in hand then the `CardsLeftInPool` method is executed. If we do have a 6 and there are cards left in the pool, then we are directed to 'Go Fish' and draw a card. \n", "\n", "The `||` operator is called the **Conditional Logical OR** operator or referred to as the **OrElse** operator by the Visual Basic language. This operator behaves like the following:\n", "\n", "1. Evaluate the left-side of the operator\n", "2. IF the left-side evaluates to true, return true and stop processing\n", "3. ELSE return the result of evaluating the right-side of the operator\n", "\n", "In this example, we'll check if we have any 6's. If we have any sixes OR we can draw a card from the pool, we'll report that we got additional cards." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "var poolHasMoreCards = true;\n", "display(\"My hand: \" + myHand);\n", "\n", "bool DrawCardFromPool() {\n", " display(\"DrawCardFromPool was called\");\n", " return poolHasMoreCards;\n", "}\n", "\n", "display(\"Do you have any 6's?\");\n", "bool haveAnySixes = myHand.Contains(\"6\");\n", "\n", "var cardDrawn = \"A♣️\";\n", "if (haveAnySixes || DrawCardFromPool()) {\n", " display(\"Got some cards!\");\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Conditional Operators\n", "\n", "There are a number of operators that you can use to perform one-line interactions to conditionally assign values to variables or for using in other expressions. While these operators make your code shorter and sometimes easier to read, other times it can make for confusing statements when you chain together a number of these operators. As with any language feature or tool, a little bit goes a long way.\n", "\n", "#### The Ternary Conditional Operator ? :\n", "\n", "The [**Conditional Operator**](https://docs.microsoft.com/dotnet/csharp/language-reference/operators/conditional-operator?WT.mc_id=visualstudio-twitch-jefritz) evaluates the term to the left of the `?` and returns the value between the `?` and `:` if the condition returns true. It returns the value to the right of the `:` if false. The following format makes this a little clearer:\n", "\n", "```\n", "is this true ? yes : no\n", "```\n", "\n", "Let's take a look at a sample:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "display(\"My hand: \" + myHand);\n", "\n", "// test if we have any 6's\n", "display(\"Do you have any 6's?\");\n", "// if true\n", "// if false\n", "var response = myHand.Contains(\"6\") ? \"Yes, here is my 6\" : \"Nope... Go fish!\";\n", "display(response);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Switch Statements\n", "\n", "Sometimes we have a LOT of conditions and branches that we want to evaluate and potentially traverse in our code. The [**switch statement**](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/switch?WT.mc_id=visualstudio-twitch-jefritz) allows you to configure using the `switch`, `case`, `break`, and `default` statements the various branches you could potentially step down.\n", "\n", "Use `switch (test expression)` to perform your test. Then use a series of `case (result):` statements to provide the various branching code paths to potentially execute. You can allow processing to 'fall out' of one statement into the next, and even provide a `default` branch at the end to ensure a branch is executed if none of the cases are matched.\n", "\n", "Let's look at a real example that inspects the card we drew from the pool. In the cases where the card is a 6 of diamonds, clubs, hearts, or spades let's celebrate! Otherwise, we'll concede that its the opponent's turn." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "\n", "display(\"My hand: \" + myHand);\n", "\n", "display(\"Do you have any 6's?\");\n", "display(\"No, go fish\");\n", "\n", "var cardDrawn = \"6♣️\";\n", "switch (cardDrawn) {\n", " case \"6♦️\":\n", " display(\"Yes! I got a 6 of Diamonds\");\n", " break;\n", " case \"6♣️\":\n", " display(\"It's a 6 of Clubs!\");\n", " break;\n", " case \"6♥️\":\n", " display(\"W00t! Its the 6 of Hearts!\");\n", " break;\n", " case \"6♠️\":\n", " display(\"6 of Spades! My lucky day\");\n", " break;\n", " default:\n", " display(\"I didn't get a 6... your turn\");\n", " break;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can add additional tests for `case` statements using a `when` clause as well. Let's consolidate the response for the 6 of diamonds, clubs, and hearts but if we get a 6 of spades AND our hand also contains a 6 we will celebrate that we have a pair of 6's. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "display(\"My hand: \" + myHand);\n", "\n", "display(\"Do you have any 6's?\");\n", "display(\"No, go fish\");\n", "\n", "var cardDrawn = \"6♣️\";\n", "switch (cardDrawn) {\n", " case \"6♦️\":\n", " case \"6♣️\":\n", " case \"6♥️\":\n", " display(\"I got a 6!\");\n", " break;\n", " case \"6♠️\" when (myHand.Contains(\"6\")):\n", " display(\"6 of Spades! That completes a pair\");\n", " break;\n", " default:\n", " display(\"I didn't get a 6... your turn\");\n", " break;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Switch Expressions\n", "\n", "What if we want a similar conditional expression, but with the power of `switch` as a simple expression to interact with our variable types? [**Switch Expressions**](https://docs.microsoft.com/dotnet/csharp/language-reference/operators/switch-expression?WT.mc_id=visualstudio-twitch-jefritz) allow us to interact with those types. This means we can assign a value based on a shorter, more terse, expression\n", "\n", "Starting with C# 8, [available only with .NET Core 3+ and .NET Standard 2.1+](https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-8?WT.mc_id=visualstudio-twitch-jefritz), you can interact with these expressions.\n", "\n", "Use the same `switch` keyword, with the value that you are evaluating and list each potential branch separated by commas `,`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var myHand = \"5♥️2♠️7♠️K♦️Q♣️\";\n", "display(\"My hand: \" + myHand);\n", "\n", "display(\"Do you have any 6's?\");\n", "display(\"No, go fish\");\n", "\n", "var cardDrawn = \"6♣️\";\n", "\n", "var message = cardDrawn[0] switch {\n", " '6' => \"I got a 6!\",\n", " 'A' => \"It's an Ace!\",\n", " _ => \"Nope.. didn't get the 6 I was looking for\"\n", "};\n", "\n", "display(message);" ] } ], "metadata": { "kernelspec": { "display_name": ".NET (C#)", "language": "C#", "name": ".net-csharp" }, "language_info": { "name": "polyglot-notebook" }, "polyglot_notebook": { "kernelInfo": { "defaultKernelName": "csharp", "items": [ { "aliases": [], "languageName": "csharp", "name": "csharp" } ] } } }, "nbformat": 4, "nbformat_minor": 2 }