diff --git a/vs4win/Chapter05/Chapter05.sln b/vs4win/Chapter05/Chapter05.sln new file mode 100644 index 0000000..7b1dc96 --- /dev/null +++ b/vs4win/Chapter05/Chapter05.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32210.308 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PacktLibrary", "PacktLibrary\PacktLibrary.csproj", "{9E796C3B-6FF6-4E0A-8B6D-A591B6FD1308}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PeopleApp", "PeopleApp\PeopleApp.csproj", "{DF7885A2-C9F1-4959-9D13-C72E5E77CCDA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9E796C3B-6FF6-4E0A-8B6D-A591B6FD1308}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E796C3B-6FF6-4E0A-8B6D-A591B6FD1308}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E796C3B-6FF6-4E0A-8B6D-A591B6FD1308}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E796C3B-6FF6-4E0A-8B6D-A591B6FD1308}.Release|Any CPU.Build.0 = Release|Any CPU + {DF7885A2-C9F1-4959-9D13-C72E5E77CCDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF7885A2-C9F1-4959-9D13-C72E5E77CCDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF7885A2-C9F1-4959-9D13-C72E5E77CCDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF7885A2-C9F1-4959-9D13-C72E5E77CCDA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1A5D8950-33D6-47DC-B740-C193A5024C06} + EndGlobalSection +EndGlobal diff --git a/vs4win/Chapter05/PacktLibrary/BankAccount.cs b/vs4win/Chapter05/PacktLibrary/BankAccount.cs new file mode 100644 index 0000000..6299ebd --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/BankAccount.cs @@ -0,0 +1,8 @@ +namespace Packt.Shared; + +public class BankAccount +{ + public string? AccountName; // instance member + public decimal Balance; // instance member + public static decimal InterestRate; // shared member +} \ No newline at end of file diff --git a/vs4win/Chapter05/PacktLibrary/Book.cs b/vs4win/Chapter05/PacktLibrary/Book.cs new file mode 100644 index 0000000..b212a2a --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/Book.cs @@ -0,0 +1,8 @@ +namespace Packt.Shared; + +public class Book +{ + // required is not working in .NET 7 Preview 1 + public /* required */ string? Isbn { get; set; } + public string? Title { get; set; } +} diff --git a/vs4win/Chapter05/PacktLibrary/FlightPatterns.cs b/vs4win/Chapter05/PacktLibrary/FlightPatterns.cs new file mode 100644 index 0000000..f582518 --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/FlightPatterns.cs @@ -0,0 +1,29 @@ +namespace Packt.Shared; + +public class BusinessClassPassenger +{ + public override string ToString() + { + return "Business Class"; + } +} + +public class FirstClassPassenger +{ + public int AirMiles { get; set; } + + public override string ToString() + { + return $"First Class with {AirMiles:N0} air miles"; + } +} + +public class CoachClassPassenger +{ + public double CarryOnKG { get; set; } + + public override string ToString() + { + return $"Coach Class with {CarryOnKG:N2} KG carry on"; + } +} diff --git a/vs4win/Chapter05/PacktLibrary/PacktLibrary.csproj b/vs4win/Chapter05/PacktLibrary/PacktLibrary.csproj new file mode 100644 index 0000000..95bce73 --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/PacktLibrary.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + 10 + enable + enable + + + + + + + diff --git a/vs4win/Chapter05/PacktLibrary/Person.cs b/vs4win/Chapter05/PacktLibrary/Person.cs new file mode 100644 index 0000000..74fe344 --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/Person.cs @@ -0,0 +1,103 @@ +namespace Packt.Shared; // file-scoped namespace + +public partial class Person : object +{ + // fields + public string? Name; + public DateTime DateOfBirth; + public WondersOfTheAncientWorld FavoriteAncientWonder; + public WondersOfTheAncientWorld BucketList; + public List Children = new(); + + // constants + public const string Species = "Homo Sapiens"; + + // read-only fields + public readonly string HomePlanet = "Earth"; + public readonly DateTime Instantiated; + + // constructors + public Person() + { + // set default values for fields + // including read-only fields + Name = "Unknown"; + Instantiated = DateTime.Now; + } + + public Person(string initialName, string homePlanet) + { + Name = initialName; + HomePlanet = homePlanet; + Instantiated = DateTime.Now; + } + + // methods + public void WriteToConsole() + { + WriteLine($"{Name} was born on a {DateOfBirth:dddd}."); + } + + public string GetOrigin() + { + return $"{Name} was born on {HomePlanet}."; + } + + public (string, int) GetFruit() + { + return ("Apples", 5); + } + + public (string Name, int Number) GetNamedFruit() + { + return (Name: "Apples", Number: 5); + } + + // deconstructors + public void Deconstruct(out string? name, out DateTime dob) + { + name = Name; + dob = DateOfBirth; + } + + public void Deconstruct(out string? name, + out DateTime dob, out WondersOfTheAncientWorld fav) + { + name = Name; + dob = DateOfBirth; + fav = FavoriteAncientWonder; + } + + public string SayHello() + { + return $"{Name} says 'Hello!'"; + } + + public string SayHello(string name) + { + return $"{Name} says 'Hello, {name}!'"; + } + + public string OptionalParameters(string command = "Run!", + double number = 0.0, bool active = true) + { + return string.Format( + format: "command is {0}, number is {1}, active is {2}", + arg0: command, + arg1: number, + arg2: active); + } + + public void PassingParameters(int x, ref int y, out int z) + { + // out parameters cannot have a default + // AND must be initialized inside the method + z = 99; + // increment each parameter + x++; + y++; + z++; + } + +} + diff --git a/vs4win/Chapter05/PacktLibrary/PersonAutoGen.cs b/vs4win/Chapter05/PacktLibrary/PersonAutoGen.cs new file mode 100644 index 0000000..4544f58 --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/PersonAutoGen.cs @@ -0,0 +1,153 @@ +namespace Packt.Shared; + +// this file simulates an autogenerated class + +public partial class Person +{ + // a readonly property defined using C# 1 - 5 syntax + public string Origin + { + get + { + return string.Format("{0} was born on {1}", + arg0: Name, arg1: HomePlanet); + } + } + + // two readonly properties defined using C# 6+ lambda expression body syntax + public string Greeting => $"{Name} says 'Hello!'"; + public int Age => DateTime.Today.Year - DateOfBirth.Year; + + // a read-write property defined using C# 3.0 syntax + public string? FavoriteIceCream { get; set; } // auto-syntax + + // a private field to store the property value + private string? favoritePrimaryColor; + + // a public property to read and write to the field + public string? FavoritePrimaryColor + { + get + { + return favoritePrimaryColor; + } + set + { + switch (value?.ToLower()) + { + case "red": + case "green": + case "blue": + favoritePrimaryColor = value; + break; + default: + throw new ArgumentException( + $"{value} is not a primary color. " + + "Choose from: red, green, blue."); + } + } + } + + // indexers + public Person this[int index] + { + get + { + return Children[index]; // pass on to the List indexer + } + set + { + Children[index] = value; + } + } + + public Person this[string name] + { + get + { + return Children.Find(p => p.Name == name); + } + set + { + Person found = Children.Find(p => p.Name == name); + if (found is not null) found = value; + } + } + + private bool married = false; + public bool Married => married; + + private Person? spouse = null; + public Person? Spouse => spouse; + + // static method to marry + public static void Marry(Person p1, Person p2) + { + p1.Marry(p2); + } + + // instance method to marry + public void Marry(Person partner) + { + if (married) return; + spouse = partner; + married = true; + partner.Marry(this); + } + + // static method to "multiply" + public static Person Procreate(Person p1, Person p2) + { + if (p1.Spouse != p2) + { + throw new ArgumentException("You must be married to procreate."); + } + + Person baby = new() + { + Name = $"Baby of {p1.Name} and {p2.Name}", + DateOfBirth = DateTime.Now + }; + + p1.Children.Add(baby); + p2.Children.Add(baby); + + return baby; + } + + // instance method to "multiply" + public Person ProcreateWith(Person partner) + { + return Procreate(this, partner); + } + + // operator to "marry" + public static bool operator +(Person p1, Person p2) + { + Marry(p1, p2); + return p1.Married && p2.Married; // confirm they are both now married + } + + // operator to "multiply" + public static Person operator *(Person p1, Person p2) + { + return Procreate(p1, p2); + } + + // method with a local function + public static int Factorial(int number) + { + if (number < 0) + { + throw new ArgumentException( + $"{nameof(number)} cannot be less than zero."); + } + return localFactorial(number); + + int localFactorial(int localNumber) // local function + { + if (localNumber == 0) return 1; + return localNumber * localFactorial(localNumber - 1); + } + } +} diff --git a/vs4win/Chapter05/PacktLibrary/Records.cs b/vs4win/Chapter05/PacktLibrary/Records.cs new file mode 100644 index 0000000..bfb2a46 --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/Records.cs @@ -0,0 +1,7 @@ +namespace Packt.Shared; + +public class ImmutablePerson +{ + //public string? FirstName { get; init; } + //public string? LastName { get; init; } +} diff --git a/vs4win/Chapter05/PacktLibrary/WondersOfTheAncientWorld.cs b/vs4win/Chapter05/PacktLibrary/WondersOfTheAncientWorld.cs new file mode 100644 index 0000000..a4b910e --- /dev/null +++ b/vs4win/Chapter05/PacktLibrary/WondersOfTheAncientWorld.cs @@ -0,0 +1,14 @@ +namespace Packt.Shared; + +[Flags] +public enum WondersOfTheAncientWorld : byte +{ + None = 0b_0000_0000, // i.e. 0 + GreatPyramidOfGiza = 0b_0000_0001, // i.e. 1 + HangingGardensOfBabylon = 0b_0000_0010, // i.e. 2 + StatueOfZeusAtOlympia = 0b_0000_0100, // i.e. 4 + TempleOfArtemisAtEphesus = 0b_0000_1000, // i.e. 8 + MausoleumAtHalicarnassus = 0b_0001_0000, // i.e. 16 + ColossusOfRhodes = 0b_0010_0000, // i.e. 32 + LighthouseOfAlexandria = 0b_0100_0000 // i.e. 64 +} diff --git a/vs4win/Chapter05/PeopleApp/PeopleApp.csproj b/vs4win/Chapter05/PeopleApp/PeopleApp.csproj new file mode 100644 index 0000000..968b8d8 --- /dev/null +++ b/vs4win/Chapter05/PeopleApp/PeopleApp.csproj @@ -0,0 +1,18 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + diff --git a/vs4win/Chapter05/PeopleApp/Program.cs b/vs4win/Chapter05/PeopleApp/Program.cs new file mode 100644 index 0000000..3a36ece --- /dev/null +++ b/vs4win/Chapter05/PeopleApp/Program.cs @@ -0,0 +1,267 @@ +using Packt.Shared; + +Thread.CurrentThread.CurrentCulture = + System.Globalization.CultureInfo.GetCultureInfo("en-GB"); + +// Person bob = new Person(); // C# 1.0 or later +// var bob = new Person(); // C# 3.0 or later + +Person bob = new(); // C# 9.0 or later +WriteLine(bob.ToString()); + +bob.Name = "Bob Smith"; +bob.DateOfBirth = new DateTime(1965, 12, 22); // C# 1.0 or later + +WriteLine(format: "{0} was born on {1:dddd, d MMMM yyyy}", + arg0: bob.Name, + arg1: bob.DateOfBirth); + +Person alice = new() +{ + Name = "Alice Jones", + DateOfBirth = new(1998, 3, 7) // C# 9.0 or later +}; + +WriteLine(format: "{0} was born on {1:dd MMM yy}", + arg0: alice.Name, + arg1: alice.DateOfBirth); + +bob.FavoriteAncientWonder = WondersOfTheAncientWorld.StatueOfZeusAtOlympia; + +WriteLine( + format: "{0}'s favorite wonder is {1}. Its integer is {2}.", + arg0: bob.Name, + arg1: bob.FavoriteAncientWonder, + arg2: (int)bob.FavoriteAncientWonder); + +bob.BucketList = + WondersOfTheAncientWorld.HangingGardensOfBabylon + | WondersOfTheAncientWorld.MausoleumAtHalicarnassus; + +// bob.BucketList = (WondersOfTheAncientWorld)18; + +WriteLine($"{bob.Name}'s bucket list is {bob.BucketList}"); + +bob.Children.Add(new Person { Name = "Alfred" }); // C# 3.0 and later +bob.Children.Add(new() { Name = "Zoe" }); // C# 9.0 and later + +WriteLine($"{bob.Name} has {bob.Children.Count} children:"); + +for (int childIndex = 0; childIndex < bob.Children.Count; childIndex++) +{ + WriteLine($"> {bob.Children[childIndex].Name}"); +} + +/* +foreach (Person child in bob.Children) +{ + WriteLine($"> {child.Name}"); +} +*/ + +BankAccount.InterestRate = 0.012M; // store a shared value + +BankAccount jonesAccount = new(); +jonesAccount.AccountName = "Mrs. Jones"; +jonesAccount.Balance = 2400; +WriteLine(format: "{0} earned {1:C} interest.", + arg0: jonesAccount.AccountName, + arg1: jonesAccount.Balance * BankAccount.InterestRate); + +BankAccount gerrierAccount = new(); +gerrierAccount.AccountName = "Ms. Gerrier"; +gerrierAccount.Balance = 98; +WriteLine(format: "{0} earned {1:C} interest.", + arg0: gerrierAccount.AccountName, + arg1: gerrierAccount.Balance * BankAccount.InterestRate); + +WriteLine($"{bob.Name} is a {Person.Species}"); + +WriteLine($"{bob.Name} was born on {bob.HomePlanet}"); + +Person blankPerson = new(); + +WriteLine(format: + "{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.", + arg0: blankPerson.Name, + arg1: blankPerson.HomePlanet, + arg2: blankPerson.Instantiated); + +Person gunny = new(initialName: "Gunny", homePlanet: "Mars"); + +WriteLine(format: + "{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.", + arg0: gunny.Name, + arg1: gunny.HomePlanet, + arg2: gunny.Instantiated); + +bob.WriteToConsole(); +WriteLine(bob.GetOrigin()); + +(string, int) fruit = bob.GetFruit(); +WriteLine($"{fruit.Item1}, {fruit.Item2} there are."); + +var fruitNamed = bob.GetNamedFruit(); +WriteLine($"There are {fruitNamed.Number} {fruitNamed.Name}."); + +var thing1 = ("Neville", 4); +WriteLine($"{thing1.Item1} has {thing1.Item2} children."); + +var thing2 = (bob.Name, bob.Children.Count); +WriteLine($"{thing2.Name} has {thing2.Count} children."); + +(string fruitName, int fruitNumber) = bob.GetFruit(); +WriteLine($"Deconstructed: {fruitName}, {fruitNumber}"); + +// Deconstructing a Person +var (name1, dob1) = bob; +WriteLine($"Deconstructed: {name1}, {dob1}"); + +var (name2, dob2, fav2) = bob; +WriteLine($"Deconstructed: {name2}, {dob2}, {fav2}"); + +WriteLine(bob.SayHello()); +WriteLine(bob.SayHello("Emily")); + +WriteLine(bob.OptionalParameters()); + +WriteLine(bob.OptionalParameters("Jump!", 98.5)); + +WriteLine(bob.OptionalParameters(number: 52.7, command: "Hide!")); + +WriteLine(bob.OptionalParameters("Poke!", active: false)); + +int a = 10; +int b = 20; +int c = 30; +WriteLine($"Before: a = {a}, b = {b}, c = {c}"); +bob.PassingParameters(a, ref b, out c); +WriteLine($"After: a = {a}, b = {b}, c = {c}"); + +int d = 10; +int e = 20; +WriteLine($"Before: d = {d}, e = {e}, f doesn't exist yet!"); + +// simplified C# 7.0 or later syntax for the out parameter +bob.PassingParameters(d, ref e, out int f); +WriteLine($"After: d = {d}, e = {e}, f = {f}"); + +Person sam = new() +{ + Name = "Sam", + DateOfBirth = new(1969, 6, 25) +}; + +WriteLine(sam.Origin); +WriteLine(sam.Greeting); +WriteLine(sam.Age); + +sam.FavoriteIceCream = "Chocolate Fudge"; +WriteLine($"Sam's favorite ice-cream flavor is {sam.FavoriteIceCream}."); +sam.FavoritePrimaryColor = "Red"; +WriteLine($"Sam's favorite primary color is {sam.FavoritePrimaryColor}."); + +Book book = new(); +book.Title = "C# 11 and .NET 7 - Modern Cross-Platform Development"; + +sam.Children.Add(new() { Name = "Charlie", DateOfBirth = new(2010, 3, 18) }); +sam.Children.Add(new() { Name = "Ella", DateOfBirth = new(2020, 12, 24) }); + +// get using Children list +WriteLine($"Sam's first child is {sam.Children[0].Name}."); +WriteLine($"Sam's second child is {sam.Children[1].Name}."); + +// get using integer position indexer +WriteLine($"Sam's first child is {sam[0].Name}."); +WriteLine($"Sam's second child is {sam[1].Name}."); + +// get using name indexer +WriteLine($"Sam's child named Ella is {sam["Ella"].Age} years old."); + +// Implementing functionality using methods + +Person lamech = new() { Name = "Lamech" }; +Person adah = new() { Name = "Adah" }; +Person zillah = new() { Name = "Zillah" }; + +lamech.Marry(adah); +// Person.Marry(zillah, lamech); +if (zillah + lamech) +{ + WriteLine($"{zillah.Name} and {lamech.Name} successfully got married."); +} + +WriteLine($"{lamech.Name} is married to {lamech.Spouse?.Name ?? "nobody"}"); +WriteLine($"{adah.Name} is married to {adah.Spouse?.Name ?? "nobody"}"); +WriteLine($"{zillah.Name} is married to {zillah.Spouse?.Name ?? "nobody"}"); + +// call instance method +Person baby1 = lamech.ProcreateWith(adah); +baby1.Name = "Jabal"; +WriteLine($"{baby1.Name} was born on {baby1.DateOfBirth}"); + +// call static method +Person baby2 = Person.Procreate(zillah, lamech); +baby2.Name = "Tubalcain"; + +// use operator to "multiply" +Person baby3 = lamech * adah; +baby3.Name = "Jubal"; + +Person baby4 = zillah * lamech; +baby4.Name = "Naamah"; + +WriteLine($"{lamech.Name} has {lamech.Children.Count} children."); +WriteLine($"{adah.Name} has {adah.Children.Count} children."); +WriteLine($"{zillah.Name} has {zillah.Children.Count} children."); + +for (int i = 0; i < lamech.Children.Count; i++) +{ + WriteLine(format: "{0}'s child #{1} is named \"{2}\".", + arg0: lamech.Name, arg1: i, arg2: lamech[i].Name); +} + +// Implementing functionality using local functions + +WriteLine($"5! is {Person.Factorial(5)}"); + +// Pattern matching with objects + +object[] passengers = { + new FirstClassPassenger { AirMiles = 1_419 }, + new FirstClassPassenger { AirMiles = 16_562 }, + new BusinessClassPassenger(), + new CoachClassPassenger { CarryOnKG = 25.7 }, + new CoachClassPassenger { CarryOnKG = 0 }, +}; + +foreach (object passenger in passengers) +{ + decimal flightCost = passenger switch + { + /* C# 8 syntax + FirstClassPassenger p when p.AirMiles > 35000 => 1500M, + FirstClassPassenger p when p.AirMiles > 15000 => 1750M, + FirstClassPassenger => 2000M, */ + + // C# 9 or later syntax + FirstClassPassenger p => p.AirMiles switch + { + > 35000 => 1500M, + > 15000 => 1750M, + _ => 2000M + }, + BusinessClassPassenger _ => 1000M, + CoachClassPassenger p when p.CarryOnKG < 10.0 => 500M, + CoachClassPassenger _ => 650M, + _ => 800M + }; + WriteLine($"Flight costs {flightCost:C} for {passenger}"); +} + +//ImmutablePerson jeff = new() +//{ +// FirstName = "Jeff", +// LastName = "Winger" +//}; +//jeff.FirstName = "Geoff"; diff --git a/vscode/Chapter05/Chapter05.code-workspace b/vscode/Chapter05/Chapter05.code-workspace new file mode 100644 index 0000000..9866d2f --- /dev/null +++ b/vscode/Chapter05/Chapter05.code-workspace @@ -0,0 +1,10 @@ +{ + "folders": [ + { + "path": "PacktLibrary" + }, + { + "path": "PeopleApp" + } + ] +} \ No newline at end of file diff --git a/vscode/Chapter05/PacktLibrary/BankAccount.cs b/vscode/Chapter05/PacktLibrary/BankAccount.cs new file mode 100644 index 0000000..6299ebd --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/BankAccount.cs @@ -0,0 +1,8 @@ +namespace Packt.Shared; + +public class BankAccount +{ + public string? AccountName; // instance member + public decimal Balance; // instance member + public static decimal InterestRate; // shared member +} \ No newline at end of file diff --git a/vscode/Chapter05/PacktLibrary/Book.cs b/vscode/Chapter05/PacktLibrary/Book.cs new file mode 100644 index 0000000..b212a2a --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/Book.cs @@ -0,0 +1,8 @@ +namespace Packt.Shared; + +public class Book +{ + // required is not working in .NET 7 Preview 1 + public /* required */ string? Isbn { get; set; } + public string? Title { get; set; } +} diff --git a/vscode/Chapter05/PacktLibrary/FlightPatterns.cs b/vscode/Chapter05/PacktLibrary/FlightPatterns.cs new file mode 100644 index 0000000..f582518 --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/FlightPatterns.cs @@ -0,0 +1,29 @@ +namespace Packt.Shared; + +public class BusinessClassPassenger +{ + public override string ToString() + { + return "Business Class"; + } +} + +public class FirstClassPassenger +{ + public int AirMiles { get; set; } + + public override string ToString() + { + return $"First Class with {AirMiles:N0} air miles"; + } +} + +public class CoachClassPassenger +{ + public double CarryOnKG { get; set; } + + public override string ToString() + { + return $"Coach Class with {CarryOnKG:N2} KG carry on"; + } +} diff --git a/vscode/Chapter05/PacktLibrary/PacktLibrary.csproj b/vscode/Chapter05/PacktLibrary/PacktLibrary.csproj new file mode 100644 index 0000000..95bce73 --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/PacktLibrary.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + 10 + enable + enable + + + + + + + diff --git a/vscode/Chapter05/PacktLibrary/Person.cs b/vscode/Chapter05/PacktLibrary/Person.cs new file mode 100644 index 0000000..74fe344 --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/Person.cs @@ -0,0 +1,103 @@ +namespace Packt.Shared; // file-scoped namespace + +public partial class Person : object +{ + // fields + public string? Name; + public DateTime DateOfBirth; + public WondersOfTheAncientWorld FavoriteAncientWonder; + public WondersOfTheAncientWorld BucketList; + public List Children = new(); + + // constants + public const string Species = "Homo Sapiens"; + + // read-only fields + public readonly string HomePlanet = "Earth"; + public readonly DateTime Instantiated; + + // constructors + public Person() + { + // set default values for fields + // including read-only fields + Name = "Unknown"; + Instantiated = DateTime.Now; + } + + public Person(string initialName, string homePlanet) + { + Name = initialName; + HomePlanet = homePlanet; + Instantiated = DateTime.Now; + } + + // methods + public void WriteToConsole() + { + WriteLine($"{Name} was born on a {DateOfBirth:dddd}."); + } + + public string GetOrigin() + { + return $"{Name} was born on {HomePlanet}."; + } + + public (string, int) GetFruit() + { + return ("Apples", 5); + } + + public (string Name, int Number) GetNamedFruit() + { + return (Name: "Apples", Number: 5); + } + + // deconstructors + public void Deconstruct(out string? name, out DateTime dob) + { + name = Name; + dob = DateOfBirth; + } + + public void Deconstruct(out string? name, + out DateTime dob, out WondersOfTheAncientWorld fav) + { + name = Name; + dob = DateOfBirth; + fav = FavoriteAncientWonder; + } + + public string SayHello() + { + return $"{Name} says 'Hello!'"; + } + + public string SayHello(string name) + { + return $"{Name} says 'Hello, {name}!'"; + } + + public string OptionalParameters(string command = "Run!", + double number = 0.0, bool active = true) + { + return string.Format( + format: "command is {0}, number is {1}, active is {2}", + arg0: command, + arg1: number, + arg2: active); + } + + public void PassingParameters(int x, ref int y, out int z) + { + // out parameters cannot have a default + // AND must be initialized inside the method + z = 99; + // increment each parameter + x++; + y++; + z++; + } + +} + diff --git a/vscode/Chapter05/PacktLibrary/PersonAutoGen.cs b/vscode/Chapter05/PacktLibrary/PersonAutoGen.cs new file mode 100644 index 0000000..4544f58 --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/PersonAutoGen.cs @@ -0,0 +1,153 @@ +namespace Packt.Shared; + +// this file simulates an autogenerated class + +public partial class Person +{ + // a readonly property defined using C# 1 - 5 syntax + public string Origin + { + get + { + return string.Format("{0} was born on {1}", + arg0: Name, arg1: HomePlanet); + } + } + + // two readonly properties defined using C# 6+ lambda expression body syntax + public string Greeting => $"{Name} says 'Hello!'"; + public int Age => DateTime.Today.Year - DateOfBirth.Year; + + // a read-write property defined using C# 3.0 syntax + public string? FavoriteIceCream { get; set; } // auto-syntax + + // a private field to store the property value + private string? favoritePrimaryColor; + + // a public property to read and write to the field + public string? FavoritePrimaryColor + { + get + { + return favoritePrimaryColor; + } + set + { + switch (value?.ToLower()) + { + case "red": + case "green": + case "blue": + favoritePrimaryColor = value; + break; + default: + throw new ArgumentException( + $"{value} is not a primary color. " + + "Choose from: red, green, blue."); + } + } + } + + // indexers + public Person this[int index] + { + get + { + return Children[index]; // pass on to the List indexer + } + set + { + Children[index] = value; + } + } + + public Person this[string name] + { + get + { + return Children.Find(p => p.Name == name); + } + set + { + Person found = Children.Find(p => p.Name == name); + if (found is not null) found = value; + } + } + + private bool married = false; + public bool Married => married; + + private Person? spouse = null; + public Person? Spouse => spouse; + + // static method to marry + public static void Marry(Person p1, Person p2) + { + p1.Marry(p2); + } + + // instance method to marry + public void Marry(Person partner) + { + if (married) return; + spouse = partner; + married = true; + partner.Marry(this); + } + + // static method to "multiply" + public static Person Procreate(Person p1, Person p2) + { + if (p1.Spouse != p2) + { + throw new ArgumentException("You must be married to procreate."); + } + + Person baby = new() + { + Name = $"Baby of {p1.Name} and {p2.Name}", + DateOfBirth = DateTime.Now + }; + + p1.Children.Add(baby); + p2.Children.Add(baby); + + return baby; + } + + // instance method to "multiply" + public Person ProcreateWith(Person partner) + { + return Procreate(this, partner); + } + + // operator to "marry" + public static bool operator +(Person p1, Person p2) + { + Marry(p1, p2); + return p1.Married && p2.Married; // confirm they are both now married + } + + // operator to "multiply" + public static Person operator *(Person p1, Person p2) + { + return Procreate(p1, p2); + } + + // method with a local function + public static int Factorial(int number) + { + if (number < 0) + { + throw new ArgumentException( + $"{nameof(number)} cannot be less than zero."); + } + return localFactorial(number); + + int localFactorial(int localNumber) // local function + { + if (localNumber == 0) return 1; + return localNumber * localFactorial(localNumber - 1); + } + } +} diff --git a/vscode/Chapter05/PacktLibrary/Records.cs b/vscode/Chapter05/PacktLibrary/Records.cs new file mode 100644 index 0000000..bfb2a46 --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/Records.cs @@ -0,0 +1,7 @@ +namespace Packt.Shared; + +public class ImmutablePerson +{ + //public string? FirstName { get; init; } + //public string? LastName { get; init; } +} diff --git a/vscode/Chapter05/PacktLibrary/WondersOfTheAncientWorld.cs b/vscode/Chapter05/PacktLibrary/WondersOfTheAncientWorld.cs new file mode 100644 index 0000000..a4b910e --- /dev/null +++ b/vscode/Chapter05/PacktLibrary/WondersOfTheAncientWorld.cs @@ -0,0 +1,14 @@ +namespace Packt.Shared; + +[Flags] +public enum WondersOfTheAncientWorld : byte +{ + None = 0b_0000_0000, // i.e. 0 + GreatPyramidOfGiza = 0b_0000_0001, // i.e. 1 + HangingGardensOfBabylon = 0b_0000_0010, // i.e. 2 + StatueOfZeusAtOlympia = 0b_0000_0100, // i.e. 4 + TempleOfArtemisAtEphesus = 0b_0000_1000, // i.e. 8 + MausoleumAtHalicarnassus = 0b_0001_0000, // i.e. 16 + ColossusOfRhodes = 0b_0010_0000, // i.e. 32 + LighthouseOfAlexandria = 0b_0100_0000 // i.e. 64 +} diff --git a/vscode/Chapter05/PeopleApp/PeopleApp.csproj b/vscode/Chapter05/PeopleApp/PeopleApp.csproj new file mode 100644 index 0000000..968b8d8 --- /dev/null +++ b/vscode/Chapter05/PeopleApp/PeopleApp.csproj @@ -0,0 +1,18 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + diff --git a/vscode/Chapter05/PeopleApp/Program.cs b/vscode/Chapter05/PeopleApp/Program.cs new file mode 100644 index 0000000..3a36ece --- /dev/null +++ b/vscode/Chapter05/PeopleApp/Program.cs @@ -0,0 +1,267 @@ +using Packt.Shared; + +Thread.CurrentThread.CurrentCulture = + System.Globalization.CultureInfo.GetCultureInfo("en-GB"); + +// Person bob = new Person(); // C# 1.0 or later +// var bob = new Person(); // C# 3.0 or later + +Person bob = new(); // C# 9.0 or later +WriteLine(bob.ToString()); + +bob.Name = "Bob Smith"; +bob.DateOfBirth = new DateTime(1965, 12, 22); // C# 1.0 or later + +WriteLine(format: "{0} was born on {1:dddd, d MMMM yyyy}", + arg0: bob.Name, + arg1: bob.DateOfBirth); + +Person alice = new() +{ + Name = "Alice Jones", + DateOfBirth = new(1998, 3, 7) // C# 9.0 or later +}; + +WriteLine(format: "{0} was born on {1:dd MMM yy}", + arg0: alice.Name, + arg1: alice.DateOfBirth); + +bob.FavoriteAncientWonder = WondersOfTheAncientWorld.StatueOfZeusAtOlympia; + +WriteLine( + format: "{0}'s favorite wonder is {1}. Its integer is {2}.", + arg0: bob.Name, + arg1: bob.FavoriteAncientWonder, + arg2: (int)bob.FavoriteAncientWonder); + +bob.BucketList = + WondersOfTheAncientWorld.HangingGardensOfBabylon + | WondersOfTheAncientWorld.MausoleumAtHalicarnassus; + +// bob.BucketList = (WondersOfTheAncientWorld)18; + +WriteLine($"{bob.Name}'s bucket list is {bob.BucketList}"); + +bob.Children.Add(new Person { Name = "Alfred" }); // C# 3.0 and later +bob.Children.Add(new() { Name = "Zoe" }); // C# 9.0 and later + +WriteLine($"{bob.Name} has {bob.Children.Count} children:"); + +for (int childIndex = 0; childIndex < bob.Children.Count; childIndex++) +{ + WriteLine($"> {bob.Children[childIndex].Name}"); +} + +/* +foreach (Person child in bob.Children) +{ + WriteLine($"> {child.Name}"); +} +*/ + +BankAccount.InterestRate = 0.012M; // store a shared value + +BankAccount jonesAccount = new(); +jonesAccount.AccountName = "Mrs. Jones"; +jonesAccount.Balance = 2400; +WriteLine(format: "{0} earned {1:C} interest.", + arg0: jonesAccount.AccountName, + arg1: jonesAccount.Balance * BankAccount.InterestRate); + +BankAccount gerrierAccount = new(); +gerrierAccount.AccountName = "Ms. Gerrier"; +gerrierAccount.Balance = 98; +WriteLine(format: "{0} earned {1:C} interest.", + arg0: gerrierAccount.AccountName, + arg1: gerrierAccount.Balance * BankAccount.InterestRate); + +WriteLine($"{bob.Name} is a {Person.Species}"); + +WriteLine($"{bob.Name} was born on {bob.HomePlanet}"); + +Person blankPerson = new(); + +WriteLine(format: + "{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.", + arg0: blankPerson.Name, + arg1: blankPerson.HomePlanet, + arg2: blankPerson.Instantiated); + +Person gunny = new(initialName: "Gunny", homePlanet: "Mars"); + +WriteLine(format: + "{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.", + arg0: gunny.Name, + arg1: gunny.HomePlanet, + arg2: gunny.Instantiated); + +bob.WriteToConsole(); +WriteLine(bob.GetOrigin()); + +(string, int) fruit = bob.GetFruit(); +WriteLine($"{fruit.Item1}, {fruit.Item2} there are."); + +var fruitNamed = bob.GetNamedFruit(); +WriteLine($"There are {fruitNamed.Number} {fruitNamed.Name}."); + +var thing1 = ("Neville", 4); +WriteLine($"{thing1.Item1} has {thing1.Item2} children."); + +var thing2 = (bob.Name, bob.Children.Count); +WriteLine($"{thing2.Name} has {thing2.Count} children."); + +(string fruitName, int fruitNumber) = bob.GetFruit(); +WriteLine($"Deconstructed: {fruitName}, {fruitNumber}"); + +// Deconstructing a Person +var (name1, dob1) = bob; +WriteLine($"Deconstructed: {name1}, {dob1}"); + +var (name2, dob2, fav2) = bob; +WriteLine($"Deconstructed: {name2}, {dob2}, {fav2}"); + +WriteLine(bob.SayHello()); +WriteLine(bob.SayHello("Emily")); + +WriteLine(bob.OptionalParameters()); + +WriteLine(bob.OptionalParameters("Jump!", 98.5)); + +WriteLine(bob.OptionalParameters(number: 52.7, command: "Hide!")); + +WriteLine(bob.OptionalParameters("Poke!", active: false)); + +int a = 10; +int b = 20; +int c = 30; +WriteLine($"Before: a = {a}, b = {b}, c = {c}"); +bob.PassingParameters(a, ref b, out c); +WriteLine($"After: a = {a}, b = {b}, c = {c}"); + +int d = 10; +int e = 20; +WriteLine($"Before: d = {d}, e = {e}, f doesn't exist yet!"); + +// simplified C# 7.0 or later syntax for the out parameter +bob.PassingParameters(d, ref e, out int f); +WriteLine($"After: d = {d}, e = {e}, f = {f}"); + +Person sam = new() +{ + Name = "Sam", + DateOfBirth = new(1969, 6, 25) +}; + +WriteLine(sam.Origin); +WriteLine(sam.Greeting); +WriteLine(sam.Age); + +sam.FavoriteIceCream = "Chocolate Fudge"; +WriteLine($"Sam's favorite ice-cream flavor is {sam.FavoriteIceCream}."); +sam.FavoritePrimaryColor = "Red"; +WriteLine($"Sam's favorite primary color is {sam.FavoritePrimaryColor}."); + +Book book = new(); +book.Title = "C# 11 and .NET 7 - Modern Cross-Platform Development"; + +sam.Children.Add(new() { Name = "Charlie", DateOfBirth = new(2010, 3, 18) }); +sam.Children.Add(new() { Name = "Ella", DateOfBirth = new(2020, 12, 24) }); + +// get using Children list +WriteLine($"Sam's first child is {sam.Children[0].Name}."); +WriteLine($"Sam's second child is {sam.Children[1].Name}."); + +// get using integer position indexer +WriteLine($"Sam's first child is {sam[0].Name}."); +WriteLine($"Sam's second child is {sam[1].Name}."); + +// get using name indexer +WriteLine($"Sam's child named Ella is {sam["Ella"].Age} years old."); + +// Implementing functionality using methods + +Person lamech = new() { Name = "Lamech" }; +Person adah = new() { Name = "Adah" }; +Person zillah = new() { Name = "Zillah" }; + +lamech.Marry(adah); +// Person.Marry(zillah, lamech); +if (zillah + lamech) +{ + WriteLine($"{zillah.Name} and {lamech.Name} successfully got married."); +} + +WriteLine($"{lamech.Name} is married to {lamech.Spouse?.Name ?? "nobody"}"); +WriteLine($"{adah.Name} is married to {adah.Spouse?.Name ?? "nobody"}"); +WriteLine($"{zillah.Name} is married to {zillah.Spouse?.Name ?? "nobody"}"); + +// call instance method +Person baby1 = lamech.ProcreateWith(adah); +baby1.Name = "Jabal"; +WriteLine($"{baby1.Name} was born on {baby1.DateOfBirth}"); + +// call static method +Person baby2 = Person.Procreate(zillah, lamech); +baby2.Name = "Tubalcain"; + +// use operator to "multiply" +Person baby3 = lamech * adah; +baby3.Name = "Jubal"; + +Person baby4 = zillah * lamech; +baby4.Name = "Naamah"; + +WriteLine($"{lamech.Name} has {lamech.Children.Count} children."); +WriteLine($"{adah.Name} has {adah.Children.Count} children."); +WriteLine($"{zillah.Name} has {zillah.Children.Count} children."); + +for (int i = 0; i < lamech.Children.Count; i++) +{ + WriteLine(format: "{0}'s child #{1} is named \"{2}\".", + arg0: lamech.Name, arg1: i, arg2: lamech[i].Name); +} + +// Implementing functionality using local functions + +WriteLine($"5! is {Person.Factorial(5)}"); + +// Pattern matching with objects + +object[] passengers = { + new FirstClassPassenger { AirMiles = 1_419 }, + new FirstClassPassenger { AirMiles = 16_562 }, + new BusinessClassPassenger(), + new CoachClassPassenger { CarryOnKG = 25.7 }, + new CoachClassPassenger { CarryOnKG = 0 }, +}; + +foreach (object passenger in passengers) +{ + decimal flightCost = passenger switch + { + /* C# 8 syntax + FirstClassPassenger p when p.AirMiles > 35000 => 1500M, + FirstClassPassenger p when p.AirMiles > 15000 => 1750M, + FirstClassPassenger => 2000M, */ + + // C# 9 or later syntax + FirstClassPassenger p => p.AirMiles switch + { + > 35000 => 1500M, + > 15000 => 1750M, + _ => 2000M + }, + BusinessClassPassenger _ => 1000M, + CoachClassPassenger p when p.CarryOnKG < 10.0 => 500M, + CoachClassPassenger _ => 650M, + _ => 800M + }; + WriteLine($"Flight costs {flightCost:C} for {passenger}"); +} + +//ImmutablePerson jeff = new() +//{ +// FirstName = "Jeff", +// LastName = "Winger" +//}; +//jeff.FirstName = "Geoff";