avnc 033 - **revisar**

This commit is contained in:
ipvg 2024-09-30 13:26:41 -03:00
parent af6b5465e2
commit ce02c6f02d
12 changed files with 624 additions and 1 deletions

View File

@ -1067,3 +1067,508 @@ Here are two important things to remember from this unit:
requirements. requirements.
--- ---
## Configure conditional breakpoints in C#
The C# debugger for Visual Studio Code supports the option to configure a
breakpoint that only triggers if a condition is met. This type of breakpoint is
called a conditional breakpoint. Conditional breakpoints can be configured
directly or by editing an existing breakpoint.
> Note
> Visual Studio Code also supports a conditional breakpoint that triggers based
on the number of times the breakpoint has been "hit".
Suppose you're debugging an application that processes product information in a
multidimensional string array. The array includes thousands of data points. The
problem that you're debugging seems to occur for products that are marked as
new. Your code processes the array inside a `for` loop. You need to set a
breakpoint inside the loop, but you only want to pause when products are `new`.
### Use a standard breakpoint to examine a data processing application
Replace the contents of your Program.cs file with the following code:
```cs
int productCount = 2000;
string[,] products = new string[productCount, 2];
LoadProducts(products, productCount);
for (int i = 0; i < productCount; i++) {
string result;
result = Process1(products, i);
if (result != "obsolete") {
result = Process2(products, i);
}
}
bool pauseCode = true;
while (pauseCode == true) ;
```
This code uses a method named `LoadProducts` to load data into the `products`
array. After the data is loaded, the code iterates through the array and calls
methods named `Process1` and `Process2`.
To generate data for the simulated processes, add the following method to the
end of your Program.cs file:
```cs
static void LoadProducts(string[,] products, int productCount) {
Random rand = new Random();
for (int i = 0; i < productCount; i++) {
int num1 = rand.Next(1, 10000) + 10000;
int num2 = rand.Next(1, 101);
string prodID = num1.ToString();
if (num2 < 91) {
products[i, 1] = "existing";
} else if (num2 == 91) {
products[i, 1] = "new";
prodID = prodID + "-n";
} else {
products[i, 1] = "obsolete";
prodID = prodID + "-0";
}
products[i, 0] = prodID;
}
}
```
The `LoadProducts` method generates 2000 random product IDs and assigns a value
of `existing`, `new`, or `obsolete` to a product description field. There is
about a 1% chance that the products are marked `new`.
To simulate data processing, add the following methods to the end of your
Program.cs file:
```cs
static string Process1(string[,] products, int item) {
Console.WriteLine(
$"Process1 message - working on {products[item, 1]} product"
);
return products[item, 1];
}
static string Process2(string[,] products, int item) {
Console.WriteLine(
$"Process2 message - working on product ID #: {products[item, 0]}"
);
if (products[item, 1] == "new") {
Process3(products, item);
}
return "continue";
}
static void Process3(string[,] products, int item) {
Console.WriteLine(
$"Process3 message - processing product information for 'new' product"
);
}
```
The `Process1` and `Process2` methods display progress messages and return a
string.
Notice that the `Process2` method calls `Process3` if the product is `new`.
On the Visual Studio Code File menu, select Save.
Near the top of the Program.cs file, set a breakpoint on the following code
line:
```cs
result = Process1(products, i);
```
Open the RUN AND DEBUG view, and then select **Start Debugging**.
<div align="center">
![img](./img/code_oss_debug_09.png)
</div>
Use Step Into to walk through the code for `Process1` and `Process2`.
Notice the updates to the VARIABLES and CALL STACK sections of the RUN AND
DEBUG view.
Continue to use Step Into to walk through the code until you see that `i` is
equal to 3.
The VARIABLES section of the RUN AND DEBUG view displays the value assigned to
`i`.
<div align="center">
![img](./img/code_oss_debug_10.png)
</div>
Notice that `Process1` and `Process2` display messages to the DEBUG CONSOLE
panel. A real application may require user interactions as data is being
processed. Some interactions may be dependent on the data being processed.
Use the **Stop** button to stop code execution.
### Configure a conditional breakpoint using an expression
A standard breakpoint is great for walking through a data processing
application. However, in this case you're interested in `new` products and you
don't want to walk through the analysis of each product to find the ones that
are `new`. This scenario is a good example of when conditional breakpoints
should be used.
Right-click your existing breakpoint, and then select `Edit Breakpoint`.
Enter the following expression:
```cs
products[i,1] == "new";
```
<div align="center">
![img](./img/code_oss_debug_11.png)
</div>
Notice that the expression is no longer displayed after you press Enter.
To display the expression temporarily, hover the mouse pointer over the
breakpoint (red dot).
<div align="center">
![img](./img/code_oss_debug_12.png)
</div>
To run your application with the conditional breakpoint configured, select
**Start Debugging**.
Wait for the application to pause at the conditional breakpoint.
Notice the value of `i` displayed under the VARIABLES section.
On the **Debug controls** toolbar, select **Continue**
Notice that the value of `i` has been updated the VARIABLES section.
<div align="center">
![img](./img/code_oss_debug_13.png)
</div>
Select **Step Into**.
Continue selecting **Step Into** until the `Process1` message is displayed.
Notice that `Process1` reports that it's working on a **new** product.
<div align="center">
![img](./img/code_oss_debug_14.png)
</div>
Take a moment to consider the advantage that conditional breakpoints offer.
In this simulated data processing scenario, there is about a 1% chance that a
product is `new`. If you're using a standard breakpoint to debug the issue,
you'd need to walk through the analysis of about 100 products to find one of
the `new` products that you're interested in.
Conditional breakpoints can save you lots of time when you're debugging an
application.
Use the **Stop** button to stop code execution.
Congratulations! You successfully configured a conditional breakpoint.
### Recap
Here are two important things to remember from this unit:
- Use a standard breakpoint to pause an application each time a breakpoint is
encountered.
- Use a conditional breakpoint to pause an application when a Boolean
expression evaluates to `true`.
---
## Exercise - Monitor variables and execution flow
The RUN AND DEBUG view provides developers with an easy way to monitor
variables and expressions, observe execution flow, and manage breakpoints
during the debug process.
Examine the sections of the Run and Debug view
Each section of the RUN AND DEBUG view provides unique capabilities. Using a combination of these sections during the debug process is often helpful.
### VARIABLES section
Monitoring variable state is an important aspect of code debugging. Unexpected
changes in variable state will often help to identify logic errors in your code.
The VARIABLES section organizes your variables by scope. The `Locals` scope
displays the variables in the current scope (the current method).
> Note
> The top-level statements section of a console application is considered its
own method. A method named `Main`.
You can unfold (expand) the displayed scopes by selecting the arrow to the left
of the scope name. You can also unfold variables and objects. The following
screenshot shows the `numbers` array unfolded under the `Locals` scope.
It's also possible to change the value of a variable at runtime using the
VARIABLES section. You can double-click the variable name and then enter a new
value.
### WATCH section
What if you want to track a variable state across time or different methods? It
can be tedious to search for the variable every time. That's where the WATCH
section comes in handy.
You can select the **Add Expression** button (appears as a plus sign: +) to
enter a variable name or an expression to watch. As an alternative, you can
right-click a variable in the VARIABLES section and select `Add to watch`.
All expressions inside the WATCH section will be updated automatically as your
code runs.
### CALL STACK section
Every time your code enters a method from another method, a call layer is added
to the application's call stack. When your application becomes complex and you
have a long list of methods called by other methods, the call stack represents
the trail of method calls.
The CALL STACK section is useful when you're trying to find the source location
for an exception or WATCH expression. If your application throws an unexpected
exception, you'll often see a message in the console that resembles the
following:
```txt
Exception has occurred: CLR/System.DivideByZeroException
An unhandled exception of type 'System.DivideByZeroException' occurred in Debug1.dll: 'Attempted to divide by zero.'
at Program.<<Main>$>g__WriteMessage|0_1() in C:\Users\howdc\Desktop\Debug1\Program.cs:line 27
at Program.<<Main>$>g__Process1|0_0() in C:\Users\howdc\Desktop\Debug1\Program.cs:line 16
at Program.<Main>$(String[] args) in C:\Users\howdc\Desktop\Debug1\Program.cs:line 10
```
The indented group of `at Program ...` lines under the error message is called
a stack trace. The stack trace lists the name and origin of every method that
was called leading up to the exception. The information can be a bit difficult
to decipher though, because it can also include information from the .NET
runtime. In this example, the stack trace is pretty clean and you can see that
exception occurred in a method named `WriteMessage`. The stack originates in a
method named `Main`, which is the top-level statements section of the console
application.
The CALL STACK section can help you to avoid the difficulty of deciphering a
stack trace that's cluttered with .NET runtime information. It filters out
unwanted information to show you only the relevant methods from your own code
by default. You can manually unwind the call stack to find out where the
exception originated.
### BREAKPOINTS section
The BREAKPOINTS section displays the current breakpoint settings and can be
used to enable or disable specific breakpoints during a debug session.
### Configure your application and launch configuration
When you're working on a console application that reads user input, you'll
probably need to update launch configuration file.
Update the code in your Program.cs file as follows:
```cs
string? readResult;
int startIndex = 0;
bool goodEntry = false;
int[] numbers = { 1, 2, 3, 4, 5 };
// Display the array to the console.
Console.Clear();
Console.Write("\n\rThe 'numbers' array contains: { ");
foreach (int number in numbers) {
Console.Write($"{number} ");
}
// To calculate a sum of array elements,
// prompt the user for the starting element number.
Console.WriteLine($"}}\n\r\n\rTo sum values 'n' through 5, enter a value for 'n':");
while (goodEntry == false) {
readResult = Console.ReadLine();
goodEntry = int.TryParse(readResult, out startIndex);
if (startIndex > 5) {
goodEntry = false;
Console.WriteLine("\n\rEnter an integer value between 1 and 5");
}
}
// Display the sum and then pause.
Console.WriteLine(
$"\n\rThe sum of numbers {startIndex} through " +
$"{numbers.Length} is: {SumValues(numbers, startIndex)}"
);
Console.WriteLine("press Enter to exit");
readResult = Console.ReadLine();
// This method returns the sum of elements n through 5
static int SumValues(int[] numbers, int n) {
int sum = 0;
for (int i = n; i < numbers.Length; i++) {
sum += numbers[i];
}
return sum;
}
```
Take a minute to review the code.
Notice the following:
- The code specifies an integer array containing five numbers.
- The code displays output in the console.
- The code prompts the user to enter a starting element number `n` that it uses
to sum array elements `n` through `5`.
- The code calculates the sum in a method, displays the results in the console,
and then pauses.
> Note
> The DEBUG CONSOLE panel does not support user input from the console.
On the Visual Studio Code **File** menu, select **Save**.
On the Run menu, select Remove All Breakpoints.
This removes any breakpoints left over from the previous exercise.
On the RUN AND DEBUG view, select Start Debugging.
Notice that an error occurs when the `Console.Clear();` code line is executed.
On the **Debug toolbar**, select **Stop**.
Switch to the EXPLORER view, and then open the launch.json file in the Editor.
Update the value of the `console` attribute as follows:
```json
"console":"integratedTerminal",
```
On the Visual Studio Code **File** menu, select **Save**, and then close the
launch.json file.
### Review application output and identify issues
Reviewing the output of your application can reveal logic issues that you've
overlooked when writing your code.
Switch back to the RUN AND DEBUG view.
On the RUN AND DEBUG view, select **Start Debugging**.
The messages displayed to the DEBUG CONSOLE panel show the debugger attaching
to the `Debug101.dll` application.
Notice that no error messages are displayed.
Changing the value of the `console` attribute from **internalConsole** to
**integratedTerminal** in the launch configuration file has fixed the console error. But now you need to locate the console that contains your output.
In the Panels area below the Editor, switch from the DEBUG CONSOLE panel to the
TERMINAL panel.
Notice that code execution has paused at the message prompting the user to
enter a value for `n`.
The output on the TERMINAL panel should look like the following:
```txt
The 'numbers' array contains: { 1 2 3 4 5 }
To sum values 'n' through 5, enter a value for 'n':
```
At the TERMINAL command prompt, enter **3**
Review the output from the application.
The output on the TERMINAL panel should look like the following:
```txt
The 'numbers' array contains: { 1 2 3 4 5 }
To sum values 'n' through 5, enter a value for 'n':
3
The sum of numbers 3 through 5 is: 9
press Enter to exit
```
Take a minute to consider the reported value of `sum` and the values of array
elements 3 through 5 displayed at the top of the console.
The message says: `The sum of numbers 3 through 5 is: 9`. However, array
elements 3 through 5 are `3`, `4`, and `5`. Shouldn't the reported sum be 12?
You can use the VARIABLES section of the RUN AND DEBUG view to investigate the
issue.
### Monitor variable state
In some cases, simply monitoring variable state is enough to identify the logic
issue in your application.
Set a breakpoint on the following code line:
```cs
Console.WriteLine($"\n\rThe sum of numbers {startIndex} through {numbers.Length} is: {SumValues(numbers, startIndex)}");
```
On the RUN AND DEBUG view, select Start **Debugging**.
Switch from the DEBUG CONSOLE panel to the TERMINAL panel.
At the TERMINAL command prompt, enter **3**
Code execution will pause at the breakpoint.
Take a minute to review the VARIABLES section of the RUN AND DEBUG view.
Notice that `startIndex` has been assigned the value that you entered, which is
`3`.
Select **Step Into**.
Notice that the VARIABLES and CALL STACK sections are updated.
The CALL STACK section shows that code execution has moved into the `SumValues`
method.
The VARIABLES section, which lists the local variables, shows the value of the
integer `n`. The method parameter `n` is assigned its value from the method
call argument `startIndex`. In this case, the change to variable names makes it
clear the value has been passed, not a reference pointer.
> Note
> In this case, you can see most of your code in the Editor, so you might not
need the CALL STACK section, but when you're working on larger applications
with deeply nested and interconnected method calls, the execution path shown in
the CALL STACK section can be extremely useful.
Continue selecting Step Into until the value assigned to `sum` is no longer `0`.
Take a minute to review the information shown in the VARIABLES section.
You should see the following:

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,41 @@
string? readResult;
int startIndex = 0;
bool goodEntry = false;
int[] numbers = { 1, 2, 3, 4, 5 };
// Display the array to the console.
Console.Clear();
Console.Write("\n\rThe 'numbers' array contains: { ");
foreach (int number in numbers) {
Console.Write($"{number} ");
}
// To calculate a sum of array elements,
// prompt the user for the starting element number.
Console.WriteLine($"}}\n\r\n\rTo sum values 'n' through 5, enter a value for 'n':");
while (goodEntry == false) {
readResult = Console.ReadLine();
goodEntry = int.TryParse(readResult, out startIndex);
if (startIndex > 5) {
goodEntry = false;
Console.WriteLine("\n\rEnter an integer value between 1 and 5");
}
}
// Display the sum and then pause.
Console.WriteLine(
$"\n\rThe sum of numbers {startIndex} through " +
$"{numbers.Length} is: {SumValues(numbers, startIndex)}"
);
Console.WriteLine("press Enter to exit");
readResult = Console.ReadLine();
// This method returns the sum of elements n through 5
static int SumValues(int[] numbers, int n) {
int sum = 0;
for (int i = n; i < numbers.Length; i++) {
sum += numbers[i];
}
return sum;
}

View File

@ -0,0 +1,57 @@
int productCount = 2000;
string[,] products = new string[productCount, 2];
LoadProducts(products, productCount);
for (int i = 0; i < productCount; i++) {
string result;
result = Process1(products, i);
if (result != "obsolete") {
result = Process2(products, i);
}
}
bool pauseCode = true;
while (pauseCode == true) ;
static void LoadProducts(string[,] products, int productCount) {
Random rand = new Random();
for (int i = 0; i < productCount; i++) {
int num1 = rand.Next(1, 10000) + 10000;
int num2 = rand.Next(1, 101);
string prodID = num1.ToString();
if (num2 < 91) {
products[i, 1] = "existing";
} else if (num2 == 91) {
products[i, 1] = "new";
prodID = prodID + "-n";
} else {
products[i, 1] = "obsolete";
prodID = prodID + "-0";
}
products[i, 0] = prodID;
}
}
static string Process1(string[,] products, int item) {
Console.WriteLine(
$"Process1 message - working on {products[item, 1]} product"
);
return products[item, 1];
}
static string Process2(string[,] products, int item) {
Console.WriteLine(
$"Process2 message - working on product ID #: {products[item, 0]}"
);
if (products[item, 1] == "new") {
Process3(products, item);
}
return "continue";
}
static void Process3(string[,] products, int item) {
Console.WriteLine(
$"Process3 message - processing product information for 'new' product"
);
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -37,4 +37,4 @@ Following
30. [Guided project - Plan a Petting Zoo](./030_project_petting_zoo/030_csharp.md) 30. [Guided project - Plan a Petting Zoo](./030_project_petting_zoo/030_csharp.md)
31. [Challenge project - Create a mini-game](./031_Challenge_mini_game/031_csharp.md) 31. [Challenge project - Create a mini-game](./031_Challenge_mini_game/031_csharp.md)
32. [Debug console applications 1](./032_Debug_console_apps/032_csharp.md) 32. [Debug console applications 1](./032_Debug_console_apps/032_csharp.md)
32. [Debug console applications 2](./032_Debug_console_apps_2/033_csharp.md) 32. [Debug console applications 2](./033_Debug_console_apps_2/033_csharp.md)