Programming C#, 2nd Edition By Jesse Liberty The unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification This page was updated December 12, 2003. UNCONFIRMED errors and comments from readers: (xvii) Near bottom of page; "... and the discussion on the C# and the .NET Framework ..." should be "... and the discussion on C# and the .NET Framework ..." (6) paragraph 6, name of the second author should be read as Hoang instead of Hoag. [12] 3rd paragraph; ... has no user interface (UI) I think it should be ... has no Graphical User Interface (GUI) <17> First paragraph; The reader is asked to save the file as Hello.cs if not using Visual Studio .NET, but on the prior page (for the Visual Studio .NET case) the reader is asked to rename the class to HelloWorld and to rename the Class1.cs file as HelloWorld. {36} 2nd paragraph; "The second way to create an unconditional branch is with one of the unconditional branch keywords: goto, break, continue, return, and throw. Additional information about the first four jump statements is provided later in the chapter..." There is no additional information about the 'return' keyword. [39] Example 3-8; Missing code? On page 38, the program spec says that if the temp is higher than 32 degrees, the program should assure you that there is no ice. The code on page 39 does not satisfy that requirement. There seems to be a blank line 2 lines up from the class's closing brace, where an else clause probably belongs to account for the temp > 32 case. (43-45) Code Sections; I would classify this as a minor formatting problem but there appears to be no such option above. The indentation of various code sections on these pages is either slightly confusing and/or does not follow the pattern of the code elsewhere in the book. For example, in 3-10 and 3-11, the code in the body of Main is indented only one character, but the return statements are indented four characters. Some of the code in 3-12 is indented 6 characters. [44] Box at top of page; I class this as serious simply because you state something that is completely wrong (though I can see it as an easy mistake!). Example 3-14 on page 43 shows code to output numbers to 100, tabing right at end and repeating every 10 numbers. The box after this shows some examples of whitespace, which are valid, but then ends with: Because single for and if statements do not need braces, we can also rewrite the same listing as: for (int i = 0; i < 100; i++) Console.Write("{0} ", i); if (i % 10 == 0) Console.WriteLine("\t{0}", i); However, without the braces in the for-loop, the variable, i, in the following if- statement is out of scope! A subject you actually show by example in the code of example 3-14 on the previous page!! I'm sure you can see what I mean... because of the semi-colon at the end of the single statement of the for-loop, you would get a line of 1 - 99, and then... well, actually, this wouldn't compile, would it?? [71] Third paragraph; On pages 70 and 72, the System.DateTime.now property is accessed in order to retrieve the current time. However, on page 71 the third paragraph says "Examine the highlighted line in Main(), where the DateTime object is created by calling the static method Now(). Now() creates a DateTime object on the heap and returns a reference to it." The highlighted line might _cause_ Now() to be called, but it's not in that code line, so without an explanation of properties, which hasn't occurred yet, the careful reader is left confused {71} 3rd paragraph the text refers to static method Now() in the DateTime class library. It should in fact refer to a static member property Now as used in the source in Example 4-3. The DateTime class does not have a static method called Now(). [72] 2nd constructor in example; I puzzled for a long time about the presence of two contructors for the same class in this example. How can a class have more than one constructor? How would the C# compiler decide which contstructor to use? I finally deduced (and confirmed by asking a friend) that the compiler would have to use the number or type of arguments in the method to decide. Finally, on page 87, the concept of overloading methods is introduced. Perhaps my confusion could have been avoided if a brief note about overloading methods had been included prior to the example (or, alternately, if a different example had been used). [76] 4th paragraph; The Time class referred to in the sentence "For example, you might add the following static constructor to Time:" comes after almost 4 pages of text and code examples that are completely unrelated to the Time example. It might be more clear to instead state "For example, you might add the following static constructor to the Time class used in Example 4-4:" [77] 3rd paragraph "Using Private Constructors"; It is confusing to find a discussion of private (non-static) constructors sandwiched in between sections on static constructors and static fields. It would be more clear to include this section in or around the tip "Resist the temptation" on page 75. (81) Second paragraph; "The idiom is to declare that objects you are using ..." should be "The idiom is to declare the objects you are using ..." (94) 1st paragraph after example code; The sentence: "As the example shows, the RightNow.Year value can be changed, for example, to 2003" lists a year that doesn't match the year used in the example code. Should be: "As the example shows, the RightNow.Year value can be changed, for example, to 2006" [97][Safari section 5.1] Figure 5-3; (Too serious to call it a typo!) Figure 5-3 is supposed to show a picture of the refactored hierarchy described on the bottom of page 96 and top of page 97. Instead, it duplicates the picture in Figure 5-2!! [104] Example 5-2: public class button; The code doesn't build correctly, the following errors occur in the Button class: 'ConsoleApplication8.Window.top' is inaccessible due to its protection level 'ConsoleApplication8.Window.left' is inaccessible due to its protection level If I replace this: public virtual void DrawWindow() ( Console.WriteLine("top: {0}, left: {1}", top, left); } with this: public virtual void DrawWindow() { base.DrawWindow(); } the problem goes away {113} 1st paragraph; "Boxing is implicit when you provide a value type where a reference is expected and the value is implicitly boxed." should read "Boxing is implicit when you provide a value type where a reference is expected." {113} code example; For the explanation after the code example to make sense, the following line in the code: Console.Writeline("The object value = {0}", i); should be Console.Writeline("The object value = {0}", i.ToString()); {133} output of Example 7.1 program; One line of the output of the program reads: In MyFunc loc: 50, 100 but my progam does not produce this. Instead it produces: Loc1 location: 50, 100 The reason is, the myFunc function's Console.WriteLine is printing it out with the text "Loc1 location". {147} Bottom of page; This is a continuation of Example 8-2. At the bottom of this page, you see "using System;" again, and at this point, all remaining code seems to be a duplication of the previous pages. Also, when the example on pages 144 thru 147 (until the "using System;" line) is keyed in, the sample program does not product the output that is exemplified on pg. 151. The ordering is different. [180-182] Tip on 182; ListBoxTest constructor on 180; The tip box on 182 advises that the "params" keyword should be used since "you cannot know how many strings will be added". It seems to me that the "params" keyword is meant to be used as a more restrictive version of the va_list found in C, and that it would be more appropriate to take the "params" keyword out. Thus, the constructor should be declared as "public ListBoxTest(string[] initStrings)", which says I am passing in an array of strings of unspecified length. Am I missing something? I don't understand why params is needed. {186} 13th line; The line currently reads "return this[findString(index)]; I believe it should read "return strings[findString(index)]; Returning this[] I think would cause an infinite loop since you would forever keep calling the indexer (192-193) Sentence that runs starts on bottom of 192 and goes to 193.; "ICollection provides four properties: Count, IsSynchronized, and SyncRoot." That should be three properties. {193} 3rd Paragraph; Book states: The IComparable interface is similar, but it defines Compare() on the object to be compared rather than on a helper class. Should be: CompareTo() instead of Compare() {226} last paragraph; Next to lastsentence indicates thatone version of substring takes two indices. One version of substring takes a starting index; the other takes a starting index and a length. [299] Example 13.1; Following line should be added at the beginning of th example using System.Drawing; {309} Figure 13-7; According to the source code of the File Copier, only the C driver can be shown in the interface. foreach ( string rootDirectoryName in strDrives ) { if ( rootDirectoryName != @"C:\" ) continue; try { ... } ... } {315} At the bottom of the page, continuing to the next page; n.Checked = check will raise the AfterCheck event for the TreeNode n. The if (n.Nodes.Count != 0) etc. is not needed and generates events that are unnecessary. If you are a fool like me and decide to change this method so that it starts off with node.Checked = check as its first statement, you are in an infinite loop and die when the stack overflows. (336) Figure 13-14; Figure 13-14 does not show the Cab file contents as intended. Instead, it shows the View menu, very similarly to Figure 13-15. (349) Third paragraph from bottom; "The DataSet has a collection of tables; you care only about the first one because you've retrieved only a single record," is misleading. You've retrieved as many *records* as there are rows in the Customers table. You've only retrieved one *table* however, which is likely what Jesse meant (DataSet.Tables[0]). Probably was trying to use "elegant substitution" for "tables", but it's best to stick with "single table" in this sentence to keep techincal terminology correct. {351} 5th line of protecte override void Dispose(bool disposing); "if (components == null)" should be "if (components != null)" otherwise components.Dispose() will never be called when needed. (402) 3rd paragraph; Mentions "United Parcel Service" as one of the options chosen, but that's not a valid option. The name in the test database is "United Package". (411) Figure 16-3; Figure 16-3 shows the result of 38^4, not 3^4 as described on page 410. {465} 4th code line from the bottom; The definition of "theType" should be defined as static. Otherwise, the code is generated and compiled 200 times- which was certainly not your intention (otherwise DoSum need not check the value or "theType"- it's always null this way). (498) code examples right after header "Using SingleCall"; First, the text there doesn't match the original code on page 495. Second, RegisterWellKnownServiceType isn't a method of RemotingServices. That should be RemotingConfiguration, i.e.: ...Here's the existing code: RemotingConfiguration.RegisterWellKnownServiceType (calcType, "theEndPoint", WellKnownObjectMode.Singleton ); Change the object to SingleCall: RemotingConfiguration.RegisterWellKnownServiceType (calcType, "theEndPoint", WellKnownObjectMode.SingleCall ); [508] Whole section on "Using Monitors"; The section on "Using Monitors" implies that the system of monitors in C# is separate from the system of locks, and that if you want to use Monitor.Wait and Monitor.Pulse, you need to use Monitor.Enter and Monitor.Exit explicitly. This is not the case. To quote the .NET framework documentation: "The functionality provided by the Enter and Exit methods is identical to that provided by the C# lock statement." In other words: lock (foo) { ... } is identical to: Monitor.Enter (foo); try { ... } finally { Monitor.Exit (foo); } Pretending there is a difference will not only lead to more confusing and more error- prone code (the former example here is clearly simpler than the latter), but will confuse Java programmers who are actually perfectly used to the C# system (with one notable change being the guaranteed order of receiving pulses in C#). The equivalence between using Monitor.Enter/Exit and using lock(...) should be made explicit, with a recommendation to use the latter wherever possible. {541} Error: FileInfotheSourceFile = Correction FileInfo theSourceFile = Description: I believe there should be a space between 'FileInfo' and 'theSourceFile'. (556) bottom of code; The declarations for the variables should be static as this is a static object initialized within main(); Just add the static keyword. {572} DeSerialize method; The DeSerialize method (repeated on pages 574 and 577) tries to call fileStream.Close() after its return statement, which is impossible. The result of binaryFormatter.Deserialize() should be stored in a local variable and returned after calling fileStream.Close(). Or a try/finally statement could be used instead, calling fileStream.Close() in the finally block. (587) Figure 22-5; Figure 22-5 doesn't show the CalcTest program running -- it shows a duplicate of Figure 22-4 instead (600) 2nd paragraph; The in-line code reads "csc /t:library /out:ProgrammingCSharpDLL.cs Calculator.cs" but it should read "csc /t:library /out:ProgrammingCSharpDLL.dll Calculator.cs" (ProgrammingCSharpDLL.cs should be ProgrammingCSharpDLL.dll). {600} Figure 22-18; The data column in the Registry Editor screen shot reads "ProgrammingCSharpDLL.Calculator" but should read "Programming_CSharp.Calculator" The values are based upon the namespace, not the DLL name