diff --git a/docs/errata/README.md b/docs/errata/README.md index b21e806..ae2f44f 100644 --- a/docs/errata/README.md +++ b/docs/errata/README.md @@ -4,7 +4,7 @@ If you find any mistakes in the seventh edition, *C# 11 and .NET 7 - Modern Cros [**Errata** (42 items)](errata.md): Typos, tool user interface changes, or mistakes in code that would cause a compilation error that prevents a successful build. -[**Improvements** (29 items)](improvements.md): Changes to text or code that would improve the content. These are optional. +[**Improvements** (30 items)](improvements.md): Changes to text or code that would improve the content. These are optional. [**Common Errors** (3 items)](common-errors.md): These are some of the most common errors that a reader might encounter when trying to get code in book tasks to work, or when trying to write your own code. diff --git a/docs/errata/improvements.md b/docs/errata/improvements.md index e661f54..1de1f4d 100644 --- a/docs/errata/improvements.md +++ b/docs/errata/improvements.md @@ -1,4 +1,4 @@ -**Improvements** (29 items) +**Improvements** (30 items) If you have suggestions for improvements, then please [raise an issue in this repository](https://github.com/markjprice/cs11dotnet7/issues) or email me at markjprice (at) gmail.com. @@ -11,6 +11,7 @@ If you have suggestions for improvements, then please [raise an issue in this re - [Page 161 - Using lambdas in function implementations](#page-161---using-lambdas-in-function-implementations) - [Page 179 - Reviewing project packages](#page-179---reviewing-project-packages) - [Page 200 - Talking about OOP](#page-200---talking-about-oop) +- [Page 235 - More about methods](#page-235---more-about-methods) - [Page 237 - Implementing functionality using methods](#page-237---implementing-functionality-using-methods) - [Page 241 - Defining flight passengers](#page-241---defining-flight-passengers) - [Page 251 - Setting up a class library and console application](#page-251---setting-up-a-class-library-and-console-application) @@ -235,6 +236,129 @@ interface ITheta { void M3() { } void M4(); } class Iota : ITheta { void M4() { } } ``` +# Page 235 - More about methods + +> Thanks to [cgwid](https://github.com/cgwid) for raising this [issue on 12 April 2023](https://github.com/markjprice/cs11dotnet7/issues/59). + +In this section, we define some methods and operators so that two `Person` objects can get married and have babies. The example we model comes from the Bible story of Lamech and his two wives and their children. But the code I tell you to write does not allow Lamech to marry two women so later an exception is thrown when Lamech and his second wife try to make a baby. + +cgwid suggested a solution in [the issue they raised](https://github.com/markjprice/cs11dotnet7/issues/59). + +Here is my possible improvement. I might change it before using it in the next edition. We could use a `List` to store the zero, one or more people that a `Person` is married too. We could implement `Married` as a readonly property that calls the LINQ `Any` method to return `true` if there are any items in the `Spouses` list, as shown in the following code: +```cs +// Is this person married to anyone? +public bool Married => Spouses.Any(); + +// Allow multiple spouses. +public List Spouses = new(); +``` + +Then we can implement the `static Marry` method by checking if the person is already in the list and then adding them if they are not, as shown in the following code: +```cs +// static method to marry +public static void Marry(Person p1, Person p2) +{ + if (p1.Spouses.Contains(p2) || p2.Spouses.Contains(p1)) + { + throw new ArgumentException( + string.Format("{0} is already married to {1}.", + arg0: p1.Name, arg1: p2.Name)); + } + else + { + p1.Spouses.Add(p2); + p2.Spouses.Add(p1); + } +} + +// instance method to marry +public void Marry(Person partner) +{ + Marry(this, partner); +} +``` + +For convenience, we could create a method to output the number of and names of spouses of the current person, as shown in the following code: +```cs +public void OutputSpouses() +{ + if (Married) + { + string term = Spouses.Count == 1 ? "person" : "people"; + WriteLine($"{Name} is married to {Spouses.Count} {term}:"); + foreach (Person spouse in Spouses) + { + WriteLine($" {spouse.Name}"); + } + } + else + { + WriteLine($"{Name} is not married."); + } +} +``` + +We would also need to change the `Procreate` method to check that the two people are married before allowing them to make a baby, as shown in the following code: +```cs +// static method to "multiply" +public static Person Procreate(Person p1, Person p2) +{ + if (!p1.Spouses.Contains(p2)) + { + throw new ArgumentException( + string.Format("{0} must be married to {1} to procreate with them.", + arg0: p1.Name, arg1: p2.Name)); + } + + Person baby = new() + { + Name = $"Baby of {p1.Name} and {p2.Name}", + DateOfBirth = DateTime.Now + }; + + p1.Children.Add(baby); + p2.Children.Add(baby); + + return baby; +} +``` + +Finally, we can call the `OutputSpouses` method in `Program.cs`, as shown in the following code: +```cs +// 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."); +} +else +{ + WriteLine($"{zillah.Name} and {lamech.Name} failed to marry."); +} + +lamech.OutputSpouses(); +adah.OutputSpouses(); +zillah.OutputSpouses(); +``` + +The output should look like this: +``` +Zillah and Lamech successfully got married. +Lamech is married to 2 people: + Adah + Zillah +Adah is married to 1 person: + Lamech +Zillah is married to 1 person: + Lamech +``` + # Page 237 - Implementing functionality using methods > Thanks to [Masoud Nazari](https://github.com/MAS-OUD) for raising this [issue on 5 March 2023](https://github.com/markjprice/cs11dotnet7/issues/35). diff --git a/vs4win/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs b/vs4win/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs index 8693aa9..b963610 100644 --- a/vs4win/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs +++ b/vs4win/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs @@ -74,33 +74,59 @@ public partial class Person } } - private bool married = false; - public bool Married => married; + // Is this person married to anyone? + public bool Married => Spouses.Any(); - private Person? spouse = null; - public Person? Spouse => spouse; + // Allow multiple spouses. + public List Spouses = new(); // static method to marry public static void Marry(Person p1, Person p2) { - p1.Marry(p2); + if (p1.Spouses.Contains(p2) || p2.Spouses.Contains(p1)) + { + throw new ArgumentException( + string.Format("{0} is already married to {1}.", + arg0: p1.Name, arg1: p2.Name)); + } + else + { + p1.Spouses.Add(p2); + p2.Spouses.Add(p1); + } } // instance method to marry public void Marry(Person partner) { - if (married) return; - spouse = partner; - married = true; - partner.Marry(this); // this is the current object + Marry(this, partner); + } + + public void OutputSpouses() + { + if (Married) + { + string term = Spouses.Count == 1 ? "person" : "people"; + WriteLine($"{Name} is married to {Spouses.Count} {term}:"); + foreach (Person spouse in Spouses) + { + WriteLine($" {spouse.Name}"); + } + } + else + { + WriteLine($"{Name} is not married."); + } } // static method to "multiply" public static Person Procreate(Person p1, Person p2) { - if (p1.Spouse != p2) + if (!p1.Spouses.Contains(p2)) { - throw new ArgumentException("You must be married to procreate."); + throw new ArgumentException( + string.Format("{0} must be married to {1} to procreate with them.", + arg0: p1.Name, arg1: p2.Name)); } Person baby = new() diff --git a/vs4win/Chapter05/PeopleApp/Program.cs b/vs4win/Chapter05/PeopleApp/Program.cs index f57c114..0c1221e 100644 --- a/vs4win/Chapter05/PeopleApp/Program.cs +++ b/vs4win/Chapter05/PeopleApp/Program.cs @@ -211,14 +211,19 @@ Person zillah = new() { Name = "Zillah" }; lamech.Marry(adah); // Person.Marry(zillah, lamech); + if (zillah + lamech) { WriteLine($"{zillah.Name} and {lamech.Name} successfully got married."); } +else +{ + WriteLine($"{zillah.Name} and {lamech.Name} failed to marry."); +} -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"}"); +lamech.OutputSpouses(); +adah.OutputSpouses(); +zillah.OutputSpouses(); // call instance method Person baby1 = lamech.ProcreateWith(adah); @@ -248,7 +253,7 @@ for (int i = 0; i < lamech.Children.Count; i++) // Implementing functionality using local functions -int number = -1; // change to -1 to make the exception handling code execute +int number = 6; // change to -1 to make the exception handling code execute try { diff --git a/vscode/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs b/vscode/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs index 8693aa9..b963610 100644 --- a/vscode/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs +++ b/vscode/Chapter05/PacktLibraryNetStandard2/PersonAutoGen.cs @@ -74,33 +74,59 @@ public partial class Person } } - private bool married = false; - public bool Married => married; + // Is this person married to anyone? + public bool Married => Spouses.Any(); - private Person? spouse = null; - public Person? Spouse => spouse; + // Allow multiple spouses. + public List Spouses = new(); // static method to marry public static void Marry(Person p1, Person p2) { - p1.Marry(p2); + if (p1.Spouses.Contains(p2) || p2.Spouses.Contains(p1)) + { + throw new ArgumentException( + string.Format("{0} is already married to {1}.", + arg0: p1.Name, arg1: p2.Name)); + } + else + { + p1.Spouses.Add(p2); + p2.Spouses.Add(p1); + } } // instance method to marry public void Marry(Person partner) { - if (married) return; - spouse = partner; - married = true; - partner.Marry(this); // this is the current object + Marry(this, partner); + } + + public void OutputSpouses() + { + if (Married) + { + string term = Spouses.Count == 1 ? "person" : "people"; + WriteLine($"{Name} is married to {Spouses.Count} {term}:"); + foreach (Person spouse in Spouses) + { + WriteLine($" {spouse.Name}"); + } + } + else + { + WriteLine($"{Name} is not married."); + } } // static method to "multiply" public static Person Procreate(Person p1, Person p2) { - if (p1.Spouse != p2) + if (!p1.Spouses.Contains(p2)) { - throw new ArgumentException("You must be married to procreate."); + throw new ArgumentException( + string.Format("{0} must be married to {1} to procreate with them.", + arg0: p1.Name, arg1: p2.Name)); } Person baby = new() diff --git a/vscode/Chapter05/PeopleApp/Program.cs b/vscode/Chapter05/PeopleApp/Program.cs index f57c114..0c1221e 100644 --- a/vscode/Chapter05/PeopleApp/Program.cs +++ b/vscode/Chapter05/PeopleApp/Program.cs @@ -211,14 +211,19 @@ Person zillah = new() { Name = "Zillah" }; lamech.Marry(adah); // Person.Marry(zillah, lamech); + if (zillah + lamech) { WriteLine($"{zillah.Name} and {lamech.Name} successfully got married."); } +else +{ + WriteLine($"{zillah.Name} and {lamech.Name} failed to marry."); +} -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"}"); +lamech.OutputSpouses(); +adah.OutputSpouses(); +zillah.OutputSpouses(); // call instance method Person baby1 = lamech.ProcreateWith(adah); @@ -248,7 +253,7 @@ for (int i = 0; i < lamech.Children.Count; i++) // Implementing functionality using local functions -int number = -1; // change to -1 to make the exception handling code execute +int number = 6; // change to -1 to make the exception handling code execute try {