mirror of
https://github.com/markjprice/cs11dotnet7.git
synced 2025-12-06 05:32:03 +01:00
879 lines
20 KiB
Plaintext
879 lines
20 KiB
Plaintext
|
|
#!markdown
|
||
|
|
|
||
|
|
# Chapter 8 - Working with Common .NET Types
|
||
|
|
|
||
|
|
Execute the following code cell to make `Console` methods available in every code cell in this notebook.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using static System.Console;
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with numbers
|
||
|
|
|
||
|
|
One of the most common types of data is numbers.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Numerics;
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Working with big integers
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine("Working with large integers:");
|
||
|
|
WriteLine("----------------------------------------");
|
||
|
|
ulong big = ulong.MaxValue;
|
||
|
|
WriteLine($"{big,40:N0}");
|
||
|
|
BigInteger bigger =
|
||
|
|
BigInteger.Parse("123456789012345678901234567890");
|
||
|
|
WriteLine($"{bigger,40:N0}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Working with complex numbers
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine("Working with complex numbers:");
|
||
|
|
Complex c1 = new(real: 4, imaginary: 2);
|
||
|
|
Complex c2 = new(real: 3, imaginary: 7);
|
||
|
|
Complex c3 = c1 + c2;
|
||
|
|
// output using default ToString implementation
|
||
|
|
WriteLine($"{c1} added to {c2} is {c3}"); // output using custom format
|
||
|
|
WriteLine("{0} + {1}i added to {2} + {3}i is {4} + {5}i",
|
||
|
|
c1.Real, c1.Imaginary,
|
||
|
|
c2.Real, c2.Imaginary,
|
||
|
|
c3.Real, c3.Imaginary);
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with text
|
||
|
|
|
||
|
|
One of the other most common types of data for variables is text.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string city = "London";
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Getting the length of a string
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine($"{city} is {city.Length} characters long.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Getting the characters of a string
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine($"First char is {city[0]} and third is {city[2]}.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Splitting a string
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string cities = "Paris,Tehran,Chennai,Sydney,New York,Medellín";
|
||
|
|
string[] citiesArray = cities.Split(',');
|
||
|
|
WriteLine($"There are {citiesArray.Length} items in the array.");
|
||
|
|
foreach (string item in citiesArray)
|
||
|
|
{
|
||
|
|
WriteLine(item);
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Getting part of a string
|
||
|
|
|
||
|
|
Sometimes, you need to get part of some text. The `IndexOf` method has nine overloads that return the index position of a specified `char` or `string` within a `string`. The `Substring` method has two overloads, as shown in the following list:
|
||
|
|
- `Substring(startIndex, length)`: returns a substring starting at startIndex and containing the next length characters.
|
||
|
|
- `Substring(startIndex)`: returns a substring starting at startIndex and containing all characters up to the end of the string.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string fullName = "Alan Jones";
|
||
|
|
int indexOfTheSpace = fullName.IndexOf(' ');
|
||
|
|
|
||
|
|
string firstName = fullName.Substring(
|
||
|
|
startIndex: 0, length: indexOfTheSpace);
|
||
|
|
|
||
|
|
string lastName = fullName.Substring(
|
||
|
|
startIndex: indexOfTheSpace + 1);
|
||
|
|
|
||
|
|
WriteLine($"Original: {fullName}");
|
||
|
|
WriteLine($"Swapped: {lastName}, {firstName}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Checking a string for content
|
||
|
|
|
||
|
|
Sometimes, you need to check whether a piece of text starts or ends with some characters or contains some characters. You can achieve this with methods named `StartsWith`, `EndsWith`, and `Contains`:
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string company = "Microsoft";
|
||
|
|
bool startsWithM = company.StartsWith("M");
|
||
|
|
bool containsN = company.Contains("N");
|
||
|
|
WriteLine($"Text: {company}");
|
||
|
|
WriteLine($"Starts with M: {startsWithM}, contains an N: {containsN}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Joining, formatting, and other string members
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string recombined = string.Join(" => ", citiesArray);
|
||
|
|
WriteLine(recombined);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string fruit = "Apples";
|
||
|
|
decimal price = 0.39M;
|
||
|
|
DateTime when = DateTime.Today;
|
||
|
|
WriteLine($"Interpolated: {fruit} cost {price:C} on {when:dddd}.");
|
||
|
|
WriteLine(string.Format("string.Format: {0} cost {1:C} on {2:dddd}.",
|
||
|
|
arg0: fruit, arg1: price, arg2: when));
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with dates and times
|
||
|
|
|
||
|
|
After numbers and text, the next most popular types of data to work with are dates and times.
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Specifying date and time values
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine("Earliest date/time value is: {0}",
|
||
|
|
arg0: DateTime.MinValue);
|
||
|
|
WriteLine("UNIX epoch date/time value is: {0}",
|
||
|
|
arg0: DateTime.UnixEpoch);
|
||
|
|
WriteLine("Date/time value Now is: {0}",
|
||
|
|
arg0: DateTime.Now);
|
||
|
|
WriteLine("Date/time value Today is: {0}",
|
||
|
|
arg0: DateTime.Today);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
DateTime christmas = new(year: 2021, month: 12, day: 25);
|
||
|
|
|
||
|
|
WriteLine("Christmas: {0}",
|
||
|
|
arg0: christmas); // default format
|
||
|
|
|
||
|
|
WriteLine("Christmas: {0:dddd, dd MMMM yyyy}",
|
||
|
|
arg0: christmas); // custom format
|
||
|
|
|
||
|
|
WriteLine("Christmas is in month {0} of the year.",
|
||
|
|
arg0: christmas.Month);
|
||
|
|
|
||
|
|
WriteLine("Christmas is day {0} of the year.",
|
||
|
|
arg0: christmas.DayOfYear);
|
||
|
|
|
||
|
|
WriteLine("Christmas {0} is on a {1}.",
|
||
|
|
arg0: christmas.Year,
|
||
|
|
arg1: christmas.DayOfWeek);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
DateTime beforeXmas = christmas.Subtract(TimeSpan.FromDays(12));
|
||
|
|
DateTime afterXmas = christmas.AddDays(12);
|
||
|
|
|
||
|
|
WriteLine("12 days before Christmas is: {0}",
|
||
|
|
arg0: beforeXmas);
|
||
|
|
|
||
|
|
WriteLine("12 days after Christmas is: {0}",
|
||
|
|
arg0: afterXmas);
|
||
|
|
|
||
|
|
TimeSpan untilChristmas = christmas - DateTime.Now;
|
||
|
|
|
||
|
|
WriteLine("There are {0} days and {1} hours until Christmas.",
|
||
|
|
arg0: untilChristmas.Days,
|
||
|
|
arg1: untilChristmas.Hours);
|
||
|
|
|
||
|
|
WriteLine("There are {0:N0} hours until Christmas.",
|
||
|
|
arg0: untilChristmas.TotalHours);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
DateTime kidsWakeUp = new(
|
||
|
|
year: 2021, month: 12, day: 25,
|
||
|
|
hour: 6, minute: 30, second: 0);
|
||
|
|
|
||
|
|
WriteLine("Kids wake up on Christmas: {0}",
|
||
|
|
arg0: kidsWakeUp);
|
||
|
|
|
||
|
|
WriteLine("The kids woke me up at {0}",
|
||
|
|
arg0: kidsWakeUp.ToShortTimeString());
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Globalization with dates and times
|
||
|
|
|
||
|
|
The current culture controls how dates and times are parsed:
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Globalization;
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
// use the following line to switch to British culture
|
||
|
|
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
|
||
|
|
|
||
|
|
WriteLine("Current culture is: {0}",
|
||
|
|
arg0: CultureInfo.CurrentCulture.Name);
|
||
|
|
|
||
|
|
string textDate = "4 July 2021";
|
||
|
|
DateTime independenceDay = DateTime.Parse(textDate);
|
||
|
|
|
||
|
|
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
|
||
|
|
arg0: textDate,
|
||
|
|
arg1: independenceDay);
|
||
|
|
|
||
|
|
textDate = "7/4/2021";
|
||
|
|
independenceDay = DateTime.Parse(textDate);
|
||
|
|
|
||
|
|
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
|
||
|
|
arg0: textDate,
|
||
|
|
arg1: independenceDay);
|
||
|
|
|
||
|
|
independenceDay = DateTime.Parse(textDate,
|
||
|
|
provider: CultureInfo.GetCultureInfo("en-US"));
|
||
|
|
|
||
|
|
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
|
||
|
|
arg0: textDate,
|
||
|
|
arg1: independenceDay);
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
for (int year = 2020; year < 2026; year++)
|
||
|
|
{
|
||
|
|
Write($"{year} is a leap year: {DateTime.IsLeapYear(year)}. ");
|
||
|
|
WriteLine("There are {0} days in February {1}.",
|
||
|
|
arg0: DateTime.DaysInMonth(year: year, month: 2), arg1: year);
|
||
|
|
}
|
||
|
|
WriteLine("Is Christmas daylight saving time? {0}",
|
||
|
|
arg0: christmas.IsDaylightSavingTime());
|
||
|
|
|
||
|
|
WriteLine("Is July 4th daylight saving time? {0}",
|
||
|
|
arg0: independenceDay.IsDaylightSavingTime());
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Working with only a date or a time
|
||
|
|
|
||
|
|
.NET 6 introduces some new types for working with only a date value or only a time value named DateOnly and TimeOnly.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
DateOnly queensBirthday = new(year: 2022, month: 4, day: 21);
|
||
|
|
WriteLine($"The Queen's next birthday is on {queensBirthday}.");
|
||
|
|
|
||
|
|
TimeOnly partyStarts = new(hour: 20, minute: 30);
|
||
|
|
WriteLine($"The Queen's party starts at {partyStarts}.");
|
||
|
|
|
||
|
|
DateTime calendarEntry = queensBirthday.ToDateTime(partyStarts);
|
||
|
|
WriteLine($"Add to your calendar: {calendarEntry}.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Pattern matching with regular expressions
|
||
|
|
|
||
|
|
Regular expressions are useful for validating input from the user.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Text.RegularExpressions;
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Checking for digits entered as text
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#nullable enable // required to support string?
|
||
|
|
|
||
|
|
// Write("Enter your age: ");
|
||
|
|
string? input = "34"; // ReadLine();
|
||
|
|
|
||
|
|
Regex ageChecker = new(@"^\d+$");
|
||
|
|
|
||
|
|
if (ageChecker.IsMatch(input))
|
||
|
|
{
|
||
|
|
WriteLine("Thank you!");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
WriteLine($"This is not a valid age: {input}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Splitting a complex comma-separated string
|
||
|
|
|
||
|
|
Earlier in this chapter, you learned how to split a simple comma-separated `string` variable. But what about the following example of film titles?
|
||
|
|
```
|
||
|
|
"Monsters, Inc.","I, Tonya","Lock, Stock and Two Smoking Barrels"
|
||
|
|
```
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string films = "\"Monsters, Inc.\",\"I, Tonya\",\"Lock, Stock and Two Smoking Barrels\"";
|
||
|
|
|
||
|
|
WriteLine($"Films to split: {films}");
|
||
|
|
|
||
|
|
string[] filmsDumb = films.Split(',');
|
||
|
|
|
||
|
|
WriteLine("Splitting with string.Split method:");
|
||
|
|
foreach (string film in filmsDumb)
|
||
|
|
{
|
||
|
|
WriteLine(film);
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WriteLine();
|
||
|
|
|
||
|
|
Regex csv = new(
|
||
|
|
"(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)");
|
||
|
|
|
||
|
|
MatchCollection filmsSmart = csv.Matches(films);
|
||
|
|
|
||
|
|
WriteLine("Splitting with regular expression:");
|
||
|
|
foreach (Match film in filmsSmart)
|
||
|
|
{
|
||
|
|
WriteLine(film.Groups[2].Value);
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Storing multiple objects in collections
|
||
|
|
|
||
|
|
Another of the most common types of data is collections. If you need to store multiple values in a variable, then you can use a collection.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
static void Output(string title, IEnumerable<string> collection)
|
||
|
|
{
|
||
|
|
WriteLine(title);
|
||
|
|
foreach (string item in collection)
|
||
|
|
{
|
||
|
|
WriteLine($" {item}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Working with lists
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
static void WorkingWithLists()
|
||
|
|
{
|
||
|
|
// Simple syntax for creating a list and adding three items
|
||
|
|
List<string> cities = new();
|
||
|
|
cities.Add("London");
|
||
|
|
cities.Add("Paris");
|
||
|
|
cities.Add("Milan");
|
||
|
|
|
||
|
|
/* Alternative syntax that is converted by the compiler into
|
||
|
|
the three Add method calls above
|
||
|
|
List<string> cities = new()
|
||
|
|
{ "London", "Paris", "Milan" };
|
||
|
|
*/
|
||
|
|
|
||
|
|
/* Alternative syntax that passes an
|
||
|
|
array of string values to AddRange method
|
||
|
|
List<string> cities = new();
|
||
|
|
cities.AddRange(new[] { "London", "Paris", "Milan" });
|
||
|
|
*/
|
||
|
|
|
||
|
|
Output("Initial list", cities);
|
||
|
|
WriteLine($"The first city is {cities[0]}.");
|
||
|
|
WriteLine($"The last city is {cities[cities.Count - 1]}.");
|
||
|
|
cities.Insert(0, "Sydney");
|
||
|
|
Output("After inserting Sydney at index 0", cities);
|
||
|
|
cities.RemoveAt(1);
|
||
|
|
cities.Remove("Milan");
|
||
|
|
Output("After removing two cities", cities);
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WorkingWithLists();
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Working with dictionaries
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
static void WorkingWithDictionaries()
|
||
|
|
{
|
||
|
|
Dictionary<string, string> keywords = new();
|
||
|
|
|
||
|
|
// add using named parameters
|
||
|
|
keywords.Add(key: "int", value: "32-bit integer data type");
|
||
|
|
|
||
|
|
// add using positional parameters
|
||
|
|
keywords.Add("long", "64-bit integer data type");
|
||
|
|
keywords.Add("float", "Single precision floating point number");
|
||
|
|
|
||
|
|
/* Alternative syntax; compiler converts this to calls to Add method
|
||
|
|
Dictionary<string, string> keywords = new()
|
||
|
|
{
|
||
|
|
{ "int", "32-bit integer data type" },
|
||
|
|
{ "long", "64-bit integer data type" },
|
||
|
|
{ "float", "Single precision floating point number" },
|
||
|
|
}; */
|
||
|
|
|
||
|
|
/* Alternative syntax; compiler converts this to calls to Add method
|
||
|
|
Dictionary<string, string> keywords = new()
|
||
|
|
{
|
||
|
|
["int"] = "32-bit integer data type",
|
||
|
|
["long"] = "64-bit integer data type",
|
||
|
|
["float"] = "Single precision floating point number"
|
||
|
|
}; */
|
||
|
|
|
||
|
|
Output("Dictionary keys:", keywords.Keys);
|
||
|
|
Output("Dictionary values:", keywords.Values);
|
||
|
|
|
||
|
|
WriteLine("Keywords and their definitions");
|
||
|
|
foreach (KeyValuePair<string, string> item in keywords)
|
||
|
|
{
|
||
|
|
WriteLine($" {item.Key}: {item.Value}");
|
||
|
|
}
|
||
|
|
|
||
|
|
// lookup a value using a key
|
||
|
|
string key = "long";
|
||
|
|
WriteLine($"The definition of {key} is {keywords[key]}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WorkingWithDictionaries();
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Working with queues
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
static void WorkingWithQueues()
|
||
|
|
{
|
||
|
|
Queue<string> coffee = new();
|
||
|
|
|
||
|
|
coffee.Enqueue("Damir"); // front of queue
|
||
|
|
coffee.Enqueue("Andrea");
|
||
|
|
coffee.Enqueue("Ronald");
|
||
|
|
coffee.Enqueue("Amin");
|
||
|
|
coffee.Enqueue("Irina"); // back of queue
|
||
|
|
|
||
|
|
Output("Initial queue from front to back", coffee);
|
||
|
|
|
||
|
|
// server handles next person in queue
|
||
|
|
string served = coffee.Dequeue();
|
||
|
|
WriteLine($"Served: {served}.");
|
||
|
|
|
||
|
|
// server handles next person in queue
|
||
|
|
served = coffee.Dequeue();
|
||
|
|
WriteLine($"Served: {served}.");
|
||
|
|
|
||
|
|
Output("Current queue from front to back", coffee);
|
||
|
|
|
||
|
|
WriteLine($"{coffee.Peek()} is next in line.");
|
||
|
|
|
||
|
|
Output("Current queue from front to back", coffee);
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WorkingWithQueues();
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
static void OutputPQ<TElement, TPriority>(string title,
|
||
|
|
IEnumerable<(TElement Element, TPriority Priority)> collection)
|
||
|
|
{
|
||
|
|
WriteLine(title);
|
||
|
|
foreach ((TElement, TPriority) item in collection)
|
||
|
|
{
|
||
|
|
WriteLine($" {item.Item1}: {item.Item2}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
static void WorkingWithPriorityQueues()
|
||
|
|
{
|
||
|
|
PriorityQueue<string, int> vaccine = new();
|
||
|
|
|
||
|
|
// add some people
|
||
|
|
// 1 = high priority people in their 70s or poor health
|
||
|
|
// 2 = medium priority e.g. middle aged
|
||
|
|
// 3 = low priority e.g. teens and twenties
|
||
|
|
vaccine.Enqueue("Pamela", 1); // my mum (70s)
|
||
|
|
vaccine.Enqueue("Rebecca", 3); // my niece (teens)
|
||
|
|
vaccine.Enqueue("Juliet", 2); // my sister (40s)
|
||
|
|
vaccine.Enqueue("Ian", 1); // my dad (70s)
|
||
|
|
|
||
|
|
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
|
||
|
|
|
||
|
|
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
|
||
|
|
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
|
||
|
|
|
||
|
|
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
|
||
|
|
|
||
|
|
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
|
||
|
|
vaccine.Enqueue("Mark", 2); // me (40s)
|
||
|
|
WriteLine($"{vaccine.Peek()} will be next to be vaccinated.");
|
||
|
|
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WorkingWithPriorityQueues();
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Using immutable collections
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Collections.Immutable;
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
static void WorkingWithLists2()
|
||
|
|
{
|
||
|
|
// Simple syntax for creating a list and adding three items
|
||
|
|
List<string> cities = new();
|
||
|
|
cities.Add("London");
|
||
|
|
cities.Add("Paris");
|
||
|
|
cities.Add("Milan");
|
||
|
|
|
||
|
|
/* Alternative syntax that is converted by the compiler into
|
||
|
|
the three Add method calls above
|
||
|
|
List<string> cities = new()
|
||
|
|
{ "London", "Paris", "Milan" };
|
||
|
|
*/
|
||
|
|
|
||
|
|
/* Alternative syntax that passes an
|
||
|
|
array of string values to AddRange method
|
||
|
|
List<string> cities = new();
|
||
|
|
cities.AddRange(new[] { "London", "Paris", "Milan" });
|
||
|
|
*/
|
||
|
|
|
||
|
|
Output("Initial list", cities);
|
||
|
|
WriteLine($"The first city is {cities[0]}.");
|
||
|
|
WriteLine($"The last city is {cities[cities.Count - 1]}.");
|
||
|
|
cities.Insert(0, "Sydney");
|
||
|
|
Output("After inserting Sydney at index 0", cities);
|
||
|
|
cities.RemoveAt(1);
|
||
|
|
cities.Remove("Milan");
|
||
|
|
Output("After removing two cities", cities);
|
||
|
|
|
||
|
|
ImmutableList<string> immutableCities = cities.ToImmutableList();
|
||
|
|
ImmutableList<string> newList = immutableCities.Add("Rio");
|
||
|
|
|
||
|
|
Output("Immutable list of cities:", immutableCities);
|
||
|
|
Output("New list of cities:", newList);
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
WorkingWithLists2();
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with spans, indexes, and ranges
|
||
|
|
|
||
|
|
C# 8.0 introduced two features for identifying an item's index within an array and a range of items using two indexes.
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Identifying ranges with the Range type
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
Range r1 = new(start: new Index(3), end: new Index(7));
|
||
|
|
Range r2 = new(start: 3, end: 7); // using implicit int conversion
|
||
|
|
Range r3 = 3..7; // using C# 8.0 or later syntax
|
||
|
|
Range r4 = Range.StartAt(3); // from index 3 to last index
|
||
|
|
Range r5 = 3..; // from index 3 to last index
|
||
|
|
Range r6 = Range.EndAt(3); // from index 0 to index 3
|
||
|
|
Range r7 = ..3; // from index 0 to index 3
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Using indexes, ranges, and spans
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string name = "Samantha Jones";
|
||
|
|
|
||
|
|
// Using Substring
|
||
|
|
|
||
|
|
int lengthOfFirst = name.IndexOf(' ');
|
||
|
|
int lengthOfLast = name.Length - lengthOfFirst - 1;
|
||
|
|
|
||
|
|
string firstName = name.Substring(
|
||
|
|
startIndex: 0,
|
||
|
|
length: lengthOfFirst);
|
||
|
|
|
||
|
|
string lastName = name.Substring(
|
||
|
|
startIndex: name.Length - lengthOfLast,
|
||
|
|
length: lengthOfLast);
|
||
|
|
|
||
|
|
WriteLine($"First name: {firstName}, Last name: {lastName}");
|
||
|
|
|
||
|
|
// Using spans
|
||
|
|
|
||
|
|
ReadOnlySpan<char> nameAsSpan = name.AsSpan();
|
||
|
|
ReadOnlySpan<char> firstNameSpan = nameAsSpan[0..lengthOfFirst];
|
||
|
|
ReadOnlySpan<char> lastNameSpan = nameAsSpan[^lengthOfLast..^0];
|
||
|
|
|
||
|
|
WriteLine("First name: {0}, Last name: {1}",
|
||
|
|
arg0: firstNameSpan.ToString(),
|
||
|
|
arg1: lastNameSpan.ToString());
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with network resources
|
||
|
|
|
||
|
|
Sometimes you will need to work with network resources.
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Working with URIs, DNS, and IP addresses
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Net; // IPHostEntry, Dns, IPAddress
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#nullable enable
|
||
|
|
|
||
|
|
// Write("Enter a valid web address: ");
|
||
|
|
string? url = "http://google.com"; // ReadLine();
|
||
|
|
|
||
|
|
if (string.IsNullOrWhiteSpace(url))
|
||
|
|
{
|
||
|
|
url = "https://stackoverflow.com/search?q=securestring";
|
||
|
|
}
|
||
|
|
|
||
|
|
Uri uri = new(url);
|
||
|
|
|
||
|
|
WriteLine($"URL: {url}");
|
||
|
|
WriteLine($"Scheme: {uri.Scheme}");
|
||
|
|
WriteLine($"Port: {uri.Port}");
|
||
|
|
WriteLine($"Host: {uri.Host}");
|
||
|
|
WriteLine($"Path: {uri.AbsolutePath}");
|
||
|
|
WriteLine($"Query: {uri.Query}");
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
IPHostEntry entry = Dns.GetHostEntry(uri.Host);
|
||
|
|
WriteLine($"{entry.HostName} has the following IP addresses:");
|
||
|
|
foreach (IPAddress address in entry.AddressList)
|
||
|
|
{
|
||
|
|
WriteLine($" {address} ({address.AddressFamily})");
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Pinging a server
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Net.NetworkInformation; // Ping, PingReply, IPStatus
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
Ping ping = new();
|
||
|
|
WriteLine("Pinging server. Please wait...");
|
||
|
|
PingReply reply = ping.Send(uri.Host);
|
||
|
|
|
||
|
|
WriteLine($"{uri.Host} was pinged and replied: {reply.Status}.");
|
||
|
|
if (reply.Status == IPStatus.Success)
|
||
|
|
{
|
||
|
|
WriteLine("Reply from {0} took {1:N0}ms",
|
||
|
|
arg0: reply.Address,
|
||
|
|
arg1: reply.RoundtripTime);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
WriteLine($"{ex.GetType().ToString()} says {ex.Message}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with reflection and attributes
|
||
|
|
|
||
|
|
Reflection is a programming feature that allows code to understand and manipulate itself.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Reflection; // Assembly
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Reading assembly metadata
|
||
|
|
|
||
|
|
Note: This example works best in a console application. In a .NET Interactive notebook, the assembly is `Microsoft.DotNet.Interactive.App`.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#nullable enable
|
||
|
|
|
||
|
|
WriteLine("Assembly metadata:");
|
||
|
|
Assembly? assembly = Assembly.GetEntryAssembly();
|
||
|
|
if (assembly is null)
|
||
|
|
{
|
||
|
|
WriteLine("Failed to get entry assembly.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
WriteLine($" Full name: {assembly.FullName}");
|
||
|
|
WriteLine($" Location: {assembly.Location}");
|
||
|
|
|
||
|
|
IEnumerable<Attribute> attributes = assembly.GetCustomAttributes();
|
||
|
|
|
||
|
|
WriteLine($" Assembly-level attributes:");
|
||
|
|
foreach (Attribute a in attributes)
|
||
|
|
{
|
||
|
|
WriteLine($" {a.GetType()}");
|
||
|
|
}
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#nullable enable
|
||
|
|
|
||
|
|
AssemblyInformationalVersionAttribute? version = assembly
|
||
|
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||
|
|
|
||
|
|
WriteLine($" Version: {version?.InformationalVersion}");
|
||
|
|
|
||
|
|
AssemblyCompanyAttribute? company = assembly
|
||
|
|
.GetCustomAttribute<AssemblyCompanyAttribute>();
|
||
|
|
|
||
|
|
WriteLine($" Company: {company?.Company}");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
## Creating custom attributes
|
||
|
|
|
||
|
|
This section should only be tried in a console application.
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Working with images
|
||
|
|
|
||
|
|
ImageSharp is a third-party cross-platform 2D graphics library.
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#r "nuget:SixLabors.ImageSharp,1.0.3"
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using SixLabors.ImageSharp;
|
||
|
|
using SixLabors.ImageSharp.Processing;
|
||
|
|
using System.IO;
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
string imagesFolder = Path.Combine(
|
||
|
|
Environment.CurrentDirectory, "images");
|
||
|
|
|
||
|
|
IEnumerable<string> images =
|
||
|
|
Directory.EnumerateFiles(imagesFolder);
|
||
|
|
|
||
|
|
foreach (string imagePath in images)
|
||
|
|
{
|
||
|
|
string thumbnailPath = Path.Combine(
|
||
|
|
Environment.CurrentDirectory, "images",
|
||
|
|
Path.GetFileNameWithoutExtension(imagePath)
|
||
|
|
+ "-thumbnail" + Path.GetExtension(imagePath));
|
||
|
|
|
||
|
|
using (Image image = Image.Load(imagePath))
|
||
|
|
{
|
||
|
|
image.Mutate(x => x.Resize(image.Width / 10, image.Height / 10));
|
||
|
|
image.Mutate(x => x.Grayscale());
|
||
|
|
image.Save(thumbnailPath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
WriteLine("Image processing complete. View the images folder.");
|
||
|
|
|
||
|
|
#!markdown
|
||
|
|
|
||
|
|
# Internationalizing your code
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
using System.Globalization; // CultureInfo
|
||
|
|
|
||
|
|
#!csharp
|
||
|
|
|
||
|
|
#nullable enable
|
||
|
|
|
||
|
|
CultureInfo globalization = CultureInfo.CurrentCulture;
|
||
|
|
CultureInfo localization = CultureInfo.CurrentUICulture;
|
||
|
|
|
||
|
|
WriteLine("The current globalization culture is {0}: {1}",
|
||
|
|
globalization.Name, globalization.DisplayName);
|
||
|
|
|
||
|
|
WriteLine("The current localization culture is {0}: {1}",
|
||
|
|
localization.Name, localization.DisplayName);
|
||
|
|
|
||
|
|
WriteLine();
|
||
|
|
|
||
|
|
WriteLine("en-US: English (United States)");
|
||
|
|
WriteLine("da-DK: Danish (Denmark)");
|
||
|
|
WriteLine("fr-CA: French (Canada)");
|
||
|
|
|
||
|
|
// Write("Enter an ISO culture code: ");
|
||
|
|
string? newCulture = "da-DK"; // ReadLine();
|
||
|
|
|
||
|
|
if (!string.IsNullOrEmpty(newCulture))
|
||
|
|
{
|
||
|
|
CultureInfo ci = new(newCulture);
|
||
|
|
|
||
|
|
// change the current cultures
|
||
|
|
CultureInfo.CurrentCulture = ci;
|
||
|
|
CultureInfo.CurrentUICulture = ci;
|
||
|
|
}
|
||
|
|
WriteLine();
|
||
|
|
|
||
|
|
// Write("Enter your name: ");
|
||
|
|
string? name = "Mikkel"; // ReadLine();
|
||
|
|
|
||
|
|
// Write("Enter your date of birth: ");
|
||
|
|
string? dob = "12/3/1980"; // ReadLine();
|
||
|
|
|
||
|
|
// Write("Enter your salary: ");
|
||
|
|
string? salary = "340000"; // ReadLine();
|
||
|
|
|
||
|
|
DateTime date = DateTime.Parse(dob);
|
||
|
|
int minutes = (int)DateTime.Today.Subtract(date).TotalMinutes;
|
||
|
|
decimal earns = decimal.Parse(salary);
|
||
|
|
|
||
|
|
WriteLine(
|
||
|
|
"{0} was born on a {1:dddd}, is {2:N0} minutes old, and earns {3:C}",
|
||
|
|
name, date, minutes, earns);
|