APPENDIX

Exercise Solutions

There are no exercises in Chapters 1 and 2.

Chapter 3 Solutions

Exercise 1

super.smashing.great

Exercise 2

b), as it starts with a number, and e), as it contains a full stop.

Exercise 3

No, there is no theoretical limit to the size of a string that may be contained in a string variable.

Exercise 4

The * and / operators have the highest precedence here, followed by +, %, and finally +=. The precedence in the exercise can be illustrated using parentheses as follows:

resultVar += (((var1 * var2) + var3) % (var4 / var5));

Exercise 5

using static System.Console;
using static System.Convert;
static void Main(string[] args)
{
   int firstNumber, secondNumber, thirdNumber, fourthNumber;
   WriteLine("Give me a number:");
   firstNumber = ToInt32(ReadLine());
   WriteLine("Give me another number:");
   secondNumber = ToInt32(Console.ReadLine());
   WriteLine("Give me another number:");
   thirdNumber = ToInt32(ReadLine());
   WriteLine("Give me another number:");
   fourthNumber = ToInt32(ReadLine());
   WriteLine($"The product of {firstNumber}, {secondNumber}, " +
           $"{thirdNumber}, and {fourthNumber} is " +
           $"{firstNumber * secondNumber * thirdNumber * fourthNumber}.");
}

Note that Convert.ToInt32() is used here, which isn't covered in the chapter.

Chapter 4 Solutions

Exercise 1

(var1 > 10) ^ (var2 > 10)

Exercise 2

using static System.Console;
using static System.Convert;
static void Main(string[] args)
{
   bool numbersOK = false;
   double var1, var2;
   var1 = 0;
   var2 = 0;
   while (!numbersOK)
   {
      WriteLine("Give me a number:");
      var1 = ToDouble(ReadLine());
      WriteLine("Give me another number:");
      var2 = ToDouble(ReadLine());
      if ((var1 > 10) && (var2 > 10))
      {
         numbersOK = true;
      }
      else
      {
         if ((var1 <= 10) && (var2 <= 10))
         {
            numbersOK = true;
         }
         else
         {
            WriteLine("Only one number may be greater than 10.");
         }
      }
   }
   WriteLine($"You entered {var1} and {var2}.");
}

Note that this can be performed better using different logic, for example:

 static void Main(string[] args)
 {
   bool numbersOK = false;
    double var1, var2;
    var1 = 0;
    var2 = 0;
    while (!numbersOK)
    {
       WriteLine("Give me a number:");
       var1 = ToDouble(ReadLine());
       WriteLine("Give me another number:");
       var2 = Convert.ToDouble(ReadLine());
       if ((var1 > 10) && (var2 > 10))
       {
          WriteLine("Only one number may be greater than 10.");
       }
       else
       {
          numbersOK = true;
       }
    }
    WriteLine($"You entered {var1} and {var2}.");
 }

Exercise 3

The code should read:

 int i;
 for (i = 1; i <= 10; i++)
 {
    if ((i % 2) == 0)
       continue;
    WriteLine(i);
 }

Using the = assignment operator instead of the Boolean == operator is a very common mistake.

Chapter 5 Solutions

Exercise 1

Conversions a and c can't be performed implicitly.

Exercise 2

 enum color : short
 {
    Red, Orange, Yellow, Green, Blue, Indigo, Violet, Black, White
 }

Yes, as the byte type can hold numbers between 0 and 255, so byte-based enumerations can hold 256 entries with individual values, or more if duplicate values are used for entries.

Exercise 3

The code will not compile, for the following reasons:

  • End of statement semicolons are missing.
  • Second line attempts to access a nonexistent sixth element of blab.
  • Second line attempts to assign a string that isn't enclosed in double quotes.

Exercise 4

using static System.Console;
static void Main(string[] args)
{
   WriteLine("Enter a string:");
   string myString = ReadLine();
   string reversedString = "";
   for (int index = myString.Length - 1; index >= 0; index--)
   {
      reversedString += myString[index];
   }
   WriteLine($"Reversed: {reversedString}");
}

Exercise 5

using static System.Console;
static void Main(string[] args)
{
   WriteLine("Enter a string:");
   string myString = ReadLine();
   myString = myString.Replace("no", "yes");
   WriteLine($"Replaced "no" with "yes": {myString}");
}

Exercise 6

using static System.Console;
static void Main(string[] args)
 {
    WriteLine("Enter a string:");
    string myString = ReadLine();
    myString = """ + myString.Replace(" ", "" "") + """;
    WriteLine($"Added double quotes around words: {myString}");
 }

Or using String.Split():

using static System.Console;
static void Main(string[] args)
 {
    WriteLine("Enter a string:");
    string myString = ReadLine();
    string[] myWords = myString.Split(' ');
    WriteLine("Adding double quotes around words:");
    foreach (string myWord in myWords)
    {
       Write($""{myWord}" ");
    }
 }

Chapter 6 Solutions

Exercise 1

The first function has a return type of bool, but doesn't return a bool value.

The second function has a params argument, but this argument isn't at the end of the argument list.

Exercise 2

using static System.Console;
static void Main(string[] args)
 {
    if (args.Length != 2)
    {
       WriteLine("Two arguments required.");
       return;
    }
    string param1 = args[0];
    int param2 = ToInt32(args[1]);
    WriteLine($"String parameter: {param1}",);
    WriteLine($"Integer parameter: {param2}",);
 }

Note that this answer contains code that checks that two arguments have been supplied, which wasn't part of the question but seems logical in this situation.

Exercise 3

class Program
{
   using static System.Console;
   delegate string ReadLineDelegate();
   static void Main(string[] args)
   {
      ReadLineDelegate readLine = new ReadLineDelegate(ReadLine);
      WriteLine("Type a string:");
      string userInput = readLine();
      WriteLine($"You typed: {userInput}");
   }
}

Exercise 4

struct order
{
   public string itemName;
   public int    unitCount;
   public double unitCost;
   public double TotalCost() => unitCount * unitCost;}

Exercise 5

struct order
{
   public string itemName;
   public int    unitCount;
   public double unitCost;
   public double TotalCost() => unitCount * unitCost;
   public string Info() => "Order information: " + unitCount.ToString() +
          " " + itemName + " items at $" + unitCost.ToString() +
          " each, total cost $" + TotalCost().ToString();
}

Chapter 7 Solutions

Exercise 1

This statement is true only for information that you want to make available in all builds. More often, you will want debugging information to be written out only when debug builds are used. In this situation, the Debug.WriteLine() version is preferable.

Using the Debug.WriteLine() version also has the advantage that it will not be compiled into release builds, thus reducing the size of the resultant code.

Exercise 2

 static void Main(string[] args)
 {
    for (int i = 1; i < 10000; i++)
    {
       WriteLine($"Loop cycle {i}");
       if (i == 5000)
       {
          WriteLine(args[999]);
       }
    }
 }

In VS, you can place a breakpoint on the following line:

            WriteLine("Loop cycle {0}", i);

The properties of the breakpoint should be modified such that the hit count criterion is “break when hit count is equal to 5000”.

Exercise 3

False. finally blocks always execute. This may occur after a catch block has been processed.

Exercise 4

 static void Main(string[] args)
 {
    Orientation myDirection;
    for (byte myByte = 2; myByte < 10; myByte++)
    {
       try
       {
          myDirection = checked((Orientation)myByte);
          if ((myDirection < Orientation.North) ║
              (myDirection > Orientation.West))
          {
             throw new ArgumentOutOfRangeException("myByte", myByte,
                "Value must be between 1 and 4");
          }
       }
       catch (ArgumentOutOfRangeException e)
       {
          // If this section is reached then myByte < 1 or myByte > 4.
          WriteLine(e.Message);
          WriteLine("Assigning default value, Orientation.North.");
          myDirection = Orientation.North;
       }
       WriteLine($"myDirection = {myDirection}");
    }
 }

Note that this is a bit of a trick question. Because the enumeration is based on the byte type, any byte value may be assigned to it, even if that value isn't assigned a name in the enumeration. In the previous code, you can generate your own exception if necessary.

Chapter 8 Solutions

Exercise 1

B, d, and e. Public, private, and protected are all real levels of accessibility.

Exercise 2

False. You should never call the destructor of an object manually; the .NET runtime environment will do this for you during garbage collection.

Exercise 3

No, you can call static methods without any class instances.

Exercise 4

Chart of class boxes CupOfCoffee and CupOfTea (with the second section filled) link to class box HotDrink (with the second and third sections filled) above. On the right is a template of the class boxes.

Figure A-1

Exercise 5

 static void ManipulateDrink(HotDrink drink)
 {
    drink.AddMilk();
    drink.Drink();
    ICup cupInterface = (ICup)drink;
    cupInterface.Wash();
 }

Note the explicit cast to ICup. This is necessary as HotDrink doesn't support the ICup interface, but you know that the two cup objects that might be passed to this function do. However, this is dangerous, as other classes deriving from HotDrink are possible, which might not support ICup, but could be passed to this function. To correct this, you should check to see if the interface is supported:

 static void ManipulateDrink(HotDrink drink)
 {
    drink.AddMilk();
    drink.Drink();
    if (drink is ICup)
    {
       ICup cupInterface = drink as ICup;
       cupInterface.Wash();
    }
 }

The is and as operators used here are covered in Chapter 11.

Chapter 9 Solutions

Exercise 1

myDerivedClass derives from MyClass, but MyClass is sealed and can't be derived from.

Exercise 2

You can define a noncreatable class by defining it as a static class or by defining all of its constructors as private.

Exercise 3

Noncreatable classes can be useful through the static members they possess. In fact, you can even get instances of these classes through these members, as shown here:

 class CreateMe
 {
    private CreateMe()
    {
    }
    static public CreateMe GetCreateMe()
    {
       return new CreateMe();
    }
 }

Here, the public constructor has access to the private constructor, as it is part of the same class definition.

Exercise 4

For simplicity, the following class definitions are shown as part of a single code file, rather than listing a separate code file for each:

namespace Vehicles
{
   public abstract class Vehicle
   {
   }
   public abstract class Car : Vehicle
   {
   }
   public abstract class Train : Vehicle
   {
   }
   public interface IPassengerCarrier
   {
   }
   public interface IHeavyLoadCarrier
   {
   }
   public class SUV : Car, IPassengerCarrier
   {
   }
   public class Pickup : Car, IPassengerCarrier, IHeavyLoadCarrier
   {
   }
   public class Compact : Car, IPassengerCarrier
   {
   }
   public class PassengerTrain : Train, IPassengerCarrier
   {
   }
   public class FreightTrain : Train, IHeavyLoadCarrier
   {
   }
   public class T424DoubleBogey : Train, IHeavyLoadCarrier
   {
   }
}

Exercise 5

using System;
using static System.Console;
using Vehicles;
namespace Traffic
{
   class Program
   {
      static void Main(string[] args)
      {
         AddPassenger(new Compact());
         AddPassenger(new SUV());
         AddPassenger(new Pickup());
         AddPassenger(new PassengerTrain());
         ReadKey();
      }
      static void AddPassenger(IPassengerCarrier Vehicle)
      {
         WriteLine(Vehicle.ToString());
      }
   }
}

Chapter 10 Solutions

Exercise 1

 class MyClass
 {
    protected string myString;
    public string ContainedString
    {
       set
       {
          myString = value;
       }
    }
    public virtual string GetString() => myString;
 }

Exercise 2

 class MyDerivedClass : MyClass
 {
    public override string GetString() => base.GetString() +
           " (output from derived class)";
 }

Exercise 3

If a method has a return type, then it is possible to use it as part of an expression:

 x = Manipulate(y, z);

If no implementation is provided for a partial method, then it will be removed by the compiler along with all places where it is used. In the preceding code this would leave the result of x unclear because no replacement for the Manipulate() method is available. It might be the case that without this method you would simply want to ignore the entire line of code, but the compiler cannot decide whether this is what you want.

Methods with no return types are not called as part of expressions, so it is safe for the compiler to remove all references to the partial method calls.

Similarly, out parameters are forbidden since variables used as an out parameter must be undefined before the method call and will be defined after the method call. Removing the method call would break this behavior.

Exercise 4

class MyCopyableClass
{
   protected int myInt;
   public int ContainedInt
   {
      get
      {
         return myInt;
      }
      set
      {
         myInt = value;
      }
   }
    public MyCopyableClass GetCopy() => (MyCopyableClass)MemberwiseClone();
}

The client code:

class Program
{
   using static System.Console;
   static void Main(string[] args)
   {
      MyCopyableClass obj1 = new MyCopyableClass();
      obj1.ContainedInt = 5;
      MyCopyableClass obj2 = obj1.GetCopy();
      obj1.ContainedInt = 9;
      WriteLine(obj2.ContainedInt);
   }
}

This code displays 5, showing that the copied object has its own version of the myInt field.

Exercise 5

 using System;
 using static System.Console;
 using Ch10CardLib;
 namespace Exercise_Answers
 {
    class Class1
    {
       static void Main(string[] args)
       {
          while(true)
          {
             Deck playDeck = new Deck();
             playDeck.Shuffle();
             bool isFlush = false;
             int flushHandIndex = 0;
             for (int hand = 0; hand < 10; hand++)
             {
                isFlush = true;
                Suit flushSuit = playDeck.GetCard(hand * 5).suit;
                for (int card = 1; card < 5; card++)
                {
                   if (playDeck.GetCard(hand * 5 + card).suit != flushSuit)
                   {
                      isFlush = false;
                   }
                }
                if (isFlush)
                {
                   flushHandIndex = hand * 5;
                   break;
                }
             }
             if (isFlush)
             {
                WriteLine("Flush!");
                for (int card = 0; card < 5; card++)
                {
                   WriteLine(playDeck.GetCard(flushHandIndex + card));
                }
             }
             else
             {
                WriteLine("No flush.");
             }
             ReadLine();
          }
       }
    }
 }

This code is looped as flushes are uncommon. You might need to press Return several times before a flush is found in a shuffled deck. To verify that everything is working as it should, try commenting out the line that shuffles the deck.

Chapter 11 Solutions

Exercise 1

using System;
using System.Collections;
namespace Exercise_Answers
{
   public class People : DictionaryBase
   {
      public void Add(Person newPerson) =>
              Dictionary.Add(newPerson.Name, newPerson);
      public void Remove(string name) => Dictionary.Remove(name);
      public Person this[string name]
      {
         get
         {
            return (Person)Dictionary[name];
         }
         set
         {
            Dictionary[name] = value;
         }
      }
   }
}

Exercise 2

public class Person
{
   private string name;
   private int age;
   public string Name
   {
      get
      {
         return name;
      }
      set
      {
         name = value;
      }
   }
   public int Age
   {
      get
      {
         return age;
      }
      set
      {
         age = value;
      }
   }
   public static bool operator >(Person p1, Person p2) =>
          p1.Age > p2.Age;
   public static bool operator <(Person p1, Person p2) =>
          p1.Age < p2.Age;
   public static bool operator >=(Person p1, Person p2) =>
          !(p1 < p2);
   public static bool operator =(Person p1, Person p2) =>
          !(p1 > p2);
}

Exercise 3

 public Person[] GetOldest()
 {
    Person oldestPerson = null;
    People oldestPeople = new People();
    Person currentPerson;
    foreach (DictionaryEntry p in Dictionary)
    {
       currentPerson = p.Value as Person;
       if (oldestPerson == null)
       {
          oldestPerson = currentPerson;
          oldestPeople.Add(oldestPerson);
       }
       else
       {
          if (currentPerson > oldestPerson)
          {
             oldestPeople.Clear();
             oldestPeople.Add(currentPerson);
             oldestPerson = currentPerson;
          }
          else
          {
          if (currentPerson >= oldestPerson)
                {
                oldestPeople.Add(currentPerson);
             }
          }
       }
    }
    Person[] oldestPeopleArray = new Person[oldestPeople.Count];
    int copyIndex = 0;
    foreach (DictionaryEntry p in oldestPeople)
    {
       oldestPeopleArray[copyIndex] = p.Value as Person;
       copyIndex++;
    }
       return oldestPeopleArray;
 }

This function is made more complex by the fact that no == operator has been defined for Person, but the logic can still be constructed without this. In addition, returning a People instance would be simpler, as it is easier to manipulate this class during processing. As a compromise, a People instance is used throughout the function, and then converted into an array of Person instances at the end.

Exercise 4

public class People : DictionaryBase, ICloneable
{
   public object Clone()
   {
      People clonedPeople = new People();
      Person currentPerson, newPerson;
      foreach (DictionaryEntry p in Dictionary)
      {
         currentPerson = p.Value as Person;
         newPerson = new Person();
         newPerson.Name = currentPerson.Name;
         newPerson.Age = currentPerson.Age;
         clonedPeople.Add(newPerson);
      }
      return clonedPeople;
   }
   …
}

You could simplify this by implementing ICloneable on the Person class.

Exercise 5

 public IEnumerable Ages
 {
    get
    {
       foreach (object person in Dictionary.Values)
          yield return (person as Person).Age;
    }
 }

Chapter 12 Solutions

Exercise 1

a, b, and e: Yes

c and d: No, although they can use generic type parameters supplied by the class containing them.

f: No

Exercise 2

 public static double? operator *(Vector op1, Vector op2)
 {
    try
    {
       double angleDiff = (double)(op2.ThetaRadians.Value –
          op1.ThetaRadians.Value);
       return op1.R.Value * op2.R.Value * Math.Cos(angleDiff);
    }
    catch
    {
       return null;
    }
 }

Exercise 3

You can't instantiate T without enforcing the new()constraint on it, which ensures that a public default constructor is available:

 public class Instantiator<T>
    where T : new()
 {
    public T instance;
    public Instantiator()
    {
       instance = new T();
    }
 }

Exercise 4

The same generic type parameter, T, is used on both the generic class and the generic method. You need to rename one or both. For example:

 public class StringGetter<U>
 {
    public string GetString<T>(T item) => item.ToString();
 }

Exercise 5

One way of doing this is as follows:

public class ShortList<T> : IList<T>
{
   protected IList<T> innerCollection;
   protected int maxSize = 10;
   public ShortList()
      : this(10)
   {
   }
   public ShortList(int size)
   {
      maxSize = size;
      innerCollection = new List<T>();
   }
   public ShortList(IEnumerable<T> list)
      : this(10, list)
   {
   }
   public ShortList(int size, IEnumerable<T> list)
   {
      maxSize = size;
      innerCollection = new List<T>(list);
      if (Count > maxSize)
      {
         ThrowTooManyItemsException();
      }
   }
   protected void ThrowTooManyItemsException()
   {
      throw new IndexOutOfRangeException(
         "Unable to add any more items, maximum size is " + maxSize.ToString()
         + " items.");
   }
   #region IList<T> Members
   public int IndexOf(T item) => innerCollection.IndexOf(item);
   public void Insert(int index, T item)
   {
      if (Count < maxSize)
      {
         innerCollection.Insert(index, item);
      }
      else
      {
         ThrowTooManyItemsException();
      }
   }
   public void RemoveAt(int index)
   {
      innerCollection.RemoveAt(index);
   }
   public T this[int index]
   {
      get
      {
         return innerCollection[index];
      }
      set
      {
         innerCollection[index] = value;
      }
   }
   #endregion
   #region ICollection<T> Members
   public void Add(T item)
   {
      if (Count < maxSize)
      {
         innerCollection.Add(item);
      }
      else
      {
         ThrowTooManyItemsException();
      }
   }
   public void Clear()
   {
      innerCollection.Clear();
   }
   public bool Contains(T item) => innerCollection.Contains(item);
   public void CopyTo(T[] array, int arrayIndex)
   {
      innerCollection.CopyTo(array, arrayIndex);
   }
   public int Count
   {
      get
      {
         return innerCollection.Count;
      }
   }
   public bool IsReadOnly
   {
      get
      {
         return innerCollection.IsReadOnly;
      }
   }
   public bool Remove(T item) => innerCollection.Remove(item);
   #endregion
   #region IEnumerable<T> Members
   public IEnumerator<T> GetEnumerator() =>
           innerCollection.GetEnumerator();
   #endregion
   #region IEnumerable Members
   IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
   #endregion
}

Exercise 6

No, it won't. The type parameter T is defined as being covariant. However, covariant type parameters can be used only as return values of methods, not as method arguments. If you try this out you will get the following compiler error (assuming you use the namespace VarianceDemo):

Invalid variance: The type parameter 'T' must be contravariantly valid on
'VarianceDemo.IMethaneProducer<T>.BelchAt(T)'. 'T' is covariant.

Chapter 13 Solutions

Exercise 1

using static System.Console;
public void ProcessEvent(object source, EventArgs e)
 {
    if (e is MessageArrivedEventArgs)
    {
       WriteLine("Connection.MessageArrived event received.");
       WriteLine($"Message: {(e as MessageArrivedEventArgs).Message }");
    }
    if (e is ElapsedEventArgs)
    {
       WriteLine("Timer.Elapsed event received.");
       WriteLine($"SignalTime: {(e as ElapsedEventArgs ).SignalTime }");
    }
 }

Exercise 2

Modify Player.cs as follows (one modified method, two new ones—comments in the code explain the changes):

 public bool HasWon()
 {
    // get temporary copy of hand, which may get modified.
    Cards tempHand = (Cards)PlayHand.Clone();
    // find three and four of a kind sets
    bool fourOfAKind = false;
    bool threeOfAKind = false;
    int fourRank = -1;
    int threeRank = -1;
    int cardsOfRank;
    for (int matchRank = 0; matchRank < 13; matchRank++)
    {
       cardsOfRank = 0;
       foreach (Card c in tempHand)
       {
          if (c.rank == (Rank)matchRank)
          {
             cardsOfRank++;
          }
       }
       if (cardsOfRank == 4)
       {
          // mark set of four
          fourRank = matchRank;
          fourOfAKind       if (cardsOfRank == 3)
       {
          // two threes means no win possible
          // (threeOfAKind will be true only if this code
          // has already executed)
          if (threeOfAKind == true)
          {
             return false;
          }
          // mark set of three
          threeRank = matchRank;
          threeOfAKind = true;
       }
    }
    // check simple win condition
    if (threeOfAKind && fourOfAKind)
    {
       return true;
    }
    // simplify hand if three or four of a kind is found,
    // by removing used cards
    if (fourOfAKind || threeOfAKind)
    {
       for (int cardIndex = tempHand.Count - 1; cardIndex >= 0; cardIndex--)
       {
          if ((tempHand[cardIndex].rank == (Rank)fourRank)
              || (tempHand[cardIndex].rank == (Rank)threeRank))
          {
             tempHand.RemoveAt(cardIndex);
          }
       }
    }
    // at this point the method may have returned, because:
    // - a set of four and a set of three has been found, winning.
    // - two sets of three have been found, losing.
    // if the method hasn't returned then:
    // - no sets have been found, and tempHand contains 7 cards.
    // - a set of three has been found, and tempHand contains 4 cards.
    // - a set of four has been found, and tempHand contains 3 cards.
    // find run of four sets, start by looking for cards of same suit
    // in the same way as before
    bool fourOfASuit = false;
    bool threeOfASuit = false;
    int fourSuit = -1;
    int threeSuit = -1;
    int cardsOfSuit;
    for (int matchSuit = 0; matchSuit < 4; matchSuit++)
    {
       cardsOfSuit = 0;
       foreach (Card c in tempHand)
       {
          if (c.suit == (Suit)matchSuit)
          {
             cardsOfSuit++;
          }
       }
       if (cardsOfSuit == 7)
       {
          // if all cards are the same suit then two runs
          // are possible, but not definite.
          threeOfASuit = true;
          threeSuit = matchSuit;
          fourOfASuit = true;
          fourSuit = matchSuit;
       }
       if (cardsOfSuit == 4)
       {
          // mark four card suit.
          fourOfASuit = true;
          fourSuit = matchSuit;
       }
       if (cardsOfSuit == 3)
       {
          // mark three card suit.
          threeOfASuit = true;
          threeSuit = matchSuit;
       }
    }
    if (!(threeOfASuit || fourOfASuit))
    {
       // need at least one run possibility to continue.
       return false;
    }
    if (tempHand.Count == 7)
    {
       if (!(threeOfASuit && fourOfASuit))
       {
          // need a three and a four card suit.
          return false;
       }
       // create two temporary sets for checking.
       Cards set1 = new Cards();
       Cards set2 = new Cards();
       // if all 7 cards are the same suit…
       if (threeSuit == fourSuit)
       {
          // get min and max cards
          int maxVal, minVal;
          GetLimits(tempHand, out maxVal, out minVal);
          for (int cardIndex = tempHand.Count - 1; cardIndex >= 0; cardIndex--)
          {
             if (((int)tempHand[cardIndex].rank < (minVal + 3))
                 || ((int)tempHand[cardIndex].rank > (maxVal - 3)))
             {
                // remove all cards in a three card set that
                // starts at minVal or ends at maxVal.
                tempHand.RemoveAt(cardIndex);
             }
          }
          if (tempHand.Count != 1)
          {
             // if more then one card is left then there aren't two runs.
             return false;
          }
          if ((tempHand[0].rank == (Rank)(minVal + 3))
              || (tempHand[0].rank == (Rank)(maxVal - 3)))
          {
             // if spare card can make one of the three card sets into a
             // four card set then there are two sets.
             return true;
          }
          else
          {
             // if spare card doesn't fit then there are two sets of three
             // cards but no set of four cards.
             return false;
          }
       }
       // if three card and four card suits are different…
       foreach (Card card in tempHand)
       {
          // split cards into sets.
          if (card.suit == (Suit)threeSuit)
          {
             set1.Add(card);
          }
          else
          {
             set2.Add(card);
          }
       }
       // check if sets are sequential.
       if (isSequential(set1) && isSequential(set2))
       {
          return true;
       }
       else
       {
          return false;
       }
    }
    // if four cards remain (three of a kind found)
    if (tempHand.Count == 4)
    {
       // if four cards remain then they must be the same suit.
       if (!fourOfASuit)
       {
          return false;
       }
       // won if cards are sequential.
       if (isSequential(tempHand))
       {
          return true;
       }
    }
    // if three cards remain (four of a kind found)
    if (tempHand.Count == 3)
    {
       // if three cards remain then they must be the same suit.
       if (!threeOfASuit)
       {
          return false;
       }
       // won if cards are sequential.
       if (isSequential(tempHand))
       {
          return true;
       }
    }
    // return false if two valid sets don't exist.
    return false;
 }
 // utility method to get max and min ranks of cards
 // (same suit assumed)
 private void GetLimits(Cards cards, out int maxVal, out int minVal)
 {
    maxVal = 0;
    minVal = 14;
    foreach (Card card in cards)
    {
       if ((int)card.rank > maxVal)
       {
          maxVal = (int)card.rank;
       }
       if ((int)card.rank < minVal)
       {
          minVal = (int)card.rank;
       }
    }
 }
 // utility method to see if cards are in a run
 // (same suit assumed)
 private bool isSequential(Cards cards)
 {
    int maxVal, minVal;
    GetLimits(cards, out maxVal, out minVal);
    if ((maxVal - minVal) == (cards.Count - 1))
    {
       return true;
    }
    else
    {
       return false;
    }
 }

Exercise 3

In order to use an object initializer with a class, you must include a default, parameter-less constructor. You could either add one to this class or remove the nondefault constructor that is there already. Once you have done this, you can use the following code to instantiate and initialize this class in one step:

Giraffe myPetGiraffe = new Giraffe
{
   NeckLength = "3.14",
   Name = "Gerald"
};

Exercise 4

False. When you use the var keyword to declare a variable, the variable is still strongly typed; the compiler determines the type of the variable.

Exercise 5

You can use the Equals() method that is implemented for you. Note that you cannot use the == operator to do this, as this compares variables to determine if they both refer to the same object.

Exercise 6

The extension method must be static:

public static string ToAcronym(this string inputString)

Exercise 7

You must include the extension method in a static class that is accessible from the namespace that contains your client code. You could do this either by including the code in the same namespace or by importing the namespace containing the class.

Exercise 8

One way to do this is as follows:

 public static string ToAcronym(this string inputString) =>
    inputString.Trim().Split(' ').Aggregate<string, string>("",
          (a, b) => a + (b.Length > 0 ?
          b.ToUpper()[0].ToString() : ""));

Here the tertiary operator prevents multiple spaces from causing errors. Note also that the version of Aggregate() with two generic type parameters is required, as a seed value is necessary.

Chapter 14 Solutions

Exercise 1

Wrap the TextBlock control in a ScrollViewer panel. Set the VerticalScrollBarVisibility property to Auto to make the scrollbar appear when the text extends beyond the bottom edge of the control.

<Window x:Class="Answers.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="14.1 Solution" Height="350" Width="525">
    <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="75"/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <Label Content="Enter text" HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top"/>
    <TextBox HorizontalAlignment="Left" Margin="76,12,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Height="53" Width="423" AcceptsReturn="True"
Name="textTextBox">
    </TextBox>
    <ScrollViewer HorizontalAlignment="Left" Height="217" Margin="10,10,0,0"
Grid.Row="1" VerticalAlignment="Top" Width="489"
VerticalScrollBarVisibility="Auto">
      <TextBlock TextWrapping="Wrap" Text="{Binding ElementName=textTextBox,
Path=Text}"/>
    </ScrollViewer>
  </Grid>
</Window>

Exercise 2

After dragging a Slider and ProgressBar control into the view, set the minimum and maximum values of the slider to 1 and 100 and the Value property to 1. Bind the same values of the ProgressBar to the Slider.

<Window x:Class="Answers. Ch14Solution2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="14.2 Solution" Height="300" Width="300">
    <Grid>
    <Slider HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"
Width="264" Minimum="1" Maximum="100" Name="valueSlider"/>
    <ProgressBar HorizontalAlignment="Left" Height="24" Margin="10,77,0,0"
VerticalAlignment="Top" Width="264"
Minimum="{Binding ElementName=valueSlider, Path=Minimum}"
Maximum="{Binding ElementName=valueSlider, Path=Maximum}"
Value="{Binding ElementName=valueSlider, Path=Value}"/>
  </Grid>
</Window>

Exercise 3

You can use a RenderTransform to do this. In Design View, you can position the cursor over the edge of the control and when you see a quarter circle icon for the mouse pointer, click and drag the control to the desired position.

<Window x:Class="Answers. Ch14Solution3"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="14.3 Solution" Height="300" Width="300">
    <Grid>
    <Slider HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"
Width="264" Minimum="1" Maximum="100" Name="valueSlider"/>
    <ProgressBar HorizontalAlignment="Left" Height="24" Margin="-17,125,-10,0"
VerticalAlignment="Top" Width="311"
Minimum="{Binding ElementName=valueSlider, Path=Minimum}" Maximum="{Binding ElementName=valueSlider, Path=Maximum}"
Value="{Binding ElementName=valueSlider, Path=Value}"
RenderTransformOrigin="0.5,0.5">
      <ProgressBar.RenderTransform>
        <TransformGroup>
          <ScaleTransform/>
          <SkewTransform/>
          <RotateTransform Angle="-36.973"/>
          <TranslateTransform/>
        </TransformGroup>
      </ProgressBar.RenderTransform>
    </ProgressBar>
  </Grid>
</Window>

Exercise 4

The PersistentSlider class must implement the INotifyPropertyChanged interface.

Create a field to hold the value of each of the three properties.

In each of the setters of the properties, implement a call to any subscribers of the PropertyChanged event. You are advised to create a helper method, called OnPropertyChanged, for this purpose.

PersistentSlider.cs:
using System.ComponentModel;
namespace Answers
{
  public class PersistentSlider : INotifyPropertyChanged
  {
    private int _minValue;
    private int _maxValue;
    private int _currentValue;
    public int MinValue
    {
      get { return _minValue; }
      set { _minValue = value; OnPropertyChanged(nameof(MinValue)); }
    }
    public int MaxValue
    {
      get { return _maxValue; }
      set { _maxValue = value; OnPropertyChanged(nameof(MaxValue)); }
    }
    public int CurrentValue
    {
      get { return _currentValue; }
      set { _currentValue = value; OnPropertyChanged(nameof(CurrentValue)); }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}
  1. In the code-behind file, add a field like this:
        private PersistentSlider _sliderData = new PersistentSlider { MinValue = 1,
    MaxValue = 200, CurrentValue = 100 };
  2. In the constructor, set the DataContext property of the current instance to the field you just created:
          this.DataContext = _sliderData;
          InitializeComponent();
  3. In the XAML, change the Slider control to use the data context. Only the Path needs to be set:
    <Window x:Class="Answers. Ch14Solution4"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="14.4 Solution" Height="300" Width="300">
      <Grid>
        <Slider HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"
    Width="264" Minimum="{Binding Path=MinValue}"
    Maximum="{Binding Path=MaxValue}" Value="{Binding Path=CurrentValue}"
    Name="valueSlider"/>
        <ProgressBar HorizontalAlignment="Left" Height="24" Margin="-17,125,-10,0"
    VerticalAlignment="Top" Width="311"
    Minimum="{Binding ElementName=valueSlider, Path=Minimum}"
    Maximum="{Binding ElementName=valueSlider, Path=Maximum}"
    Value="{Binding ElementName=valueSlider, Path=Value}"
    RenderTransformOrigin="0.5,0.5">
          <ProgressBar.RenderTransform>
            <TransformGroup>
              <ScaleTransform/>
              <SkewTransform/>
              <RotateTransform Angle="-36.973"/>
              <TranslateTransform/>
            </TransformGroup>
          </ProgressBar.RenderTransform>
        </ProgressBar>
      </Grid>
    </Window>

Chapter 15 Solutions

Exercise 1

Solution:

  1. Create a new class with the name ComputerSkillValueConverter like this:
    using Ch13CardLib;
    using System;
    using System.Windows.Data;
    namespace KarliCards_Gui
    {
      [ValueConversion(typeof(ComputerSkillLevel), typeof(bool))]
      public class ComputerSkillValueConverter : IValueConverter
      {
        public object Convert(object value, Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
        {
          string helper = parameter as string;
          if (string.IsNullOrWhiteSpace(helper))
            return false;
          ComputerSkillLevel skillLevel = (ComputerSkillLevel)value;
          return (skillLevel.ToString() == helper);
        }
        public object ConvertBack(object value, Type targetType, object parameter,
    System.Globalization.CultureInfo culture)
        {
          string parameterString = parameter as string;
          if (parameterString == null)
            return ComputerSkillLevel.Dumb;
          return Enum.Parse(targetType, parameterString);
        }
      }
    }
  2. 2. Add a static resource declaration to the Options.xaml:
      <Window.Resources>
        <src:ComputerSkillValueConverter x:Key="skillConverter"/>
      </Window.Resources>
  3. Change the radio buttons like this:
                <RadioButton Content="Dumb" HorizontalAlignment="Left"
    Margin="37,41,0,0" VerticalAlignment="Top" Name="dumbAIRadioButton"
    IsChecked="{Binding ComputerSkill, Converter={StaticResource skillConverter},
    ConverterParameter=Dumb}"/>
                <RadioButton Content="Good" HorizontalAlignment="Left"
    Margin="37,62,0,0" VerticalAlignment="Top" Name="goodAIRadioButton"
    IsChecked="{Binding ComputerSkill, Converter={StaticResource skillConverter},
    ConverterParameter=Good}"/>
                <RadioButton Content="Cheats" HorizontalAlignment="Left"
    Margin="37,83,0,0" VerticalAlignment="Top" Name="cheatingAIRadioButton" IsChecked="{Binding ComputerSkill, Converter={StaticResource skillConverter},
    ConverterParameter=Cheats}"/>
  4. Delete the events from the code-behind file.

Exercise 2

Solution:

  1. Add a new check box to the Options.xaml dialog box:
    <CheckBox Content="Plays with open cards" HorizontalAlignment="Left"
    Margin="10,100, 0,0" VerticalAlignment="Top"
    IsChecked="{Binding ComputerPlaysWithOpenHand}"/>
  2. Add a new property to the GameOptions.cs class:
        private bool _computerPlaysWithOpenHand;
        public bool ComputerPlaysWithOpenHand
        {
          get { return _computerPlaysWithOpenHand; }
          set
          {
            _computerPlaysWithOpenHand = value;
            OnPropertyChanged(nameof(ComputerPlaysWithOpenHand));
          }
    }
  3. Add a new dependency property to the CardsInHandControl:
        public bool ComputerPlaysWithOpenHand
        {
          get { return (bool)GetValue(ComputerPlaysWithOpenHandProperty); }
          set { SetValue(ComputerPlaysWithOpenHandProperty, value); }
        }
        public static readonly DependencyProperty ComputerPlaysWithOpenHandProperty =
            DependencyProperty.Register("ComputerPlaysWithOpenHand", typeof(bool),
    typeof(CardsInHandControl), new PropertyMetadata(false));
  4. 4. In the DrawCards method of the CardsInHandControl, change the test for isFaceUp:
          if (Owner is ComputerPlayer)
            isFaceup = (Owner.State == CardLib.PlayerState.Loser ||
    Owner.State == CardLib.PlayerState.Winner || ComputerPlaysWithOpenHand);
  5. Add a new property to the GameViewModel class:
        public bool ComputerPlaysWithOpenHand
        {
          get { return _gameOptions.ComputerPlaysWithOpenHand; }
        }
  6. Bind the new property to the CardsInHandControls on the game client to all four players:
    ComputerPlaysWithOpenHand="{Binding GameOptions.ComputerPlaysWithOpenHand}"

Exercise 3

Solution:

  1. Add a new property to the GameViewModel like this:
        private string _currentStatusText = "Game is not started";
        public string CurrentStatusText
        {
          get { return _currentStatusText; }
          set
          {
            _currentStatusText = value;
            OnPropertyChanged(nameof(CurrentStatusText));
          }
        }
  2. Change the CurrentPlayer property like this:
        public Player CurrentPlayer
        {
          get { return _currentPlayer; }
          set
          {
            _currentPlayer = value;
            OnPropertyChanged("CurrentPlayer");
            if (!Players.Any(x => x.State == PlayerState.Winner))
            {
              Players.ForEach(x => x.State = (x == value ? PlayerState.Active : PlayerState.Inactive));
              CurrentStatusText = $"Player {CurrentPlayer.PlayerName} ready";
            }
            else
            {
              var winner = Players.Where(x => x.HasWon).FirstOrDefault();
              if (winner != null)
                CurrentStatusText = $"Player {winner.PlayerName} has WON!";
            }
          }
        }
  3. Add this line at the end of the StartNewGame method:
    CurrentStatusText = string.Format("New game stated. Player {0} to start",
    CurrentPlayer.PlayerName);
  4. Add a status bar to the game client XAML and set the binding to the new property:
        <StatusBar Grid.Row="3" HorizontalAlignment="Center" Margin="0,0,0,15"
    VerticalAlignment="Center" Background="Green" Foreground="White" FontWeight="Bold">
            <StatusBarItem VerticalAlignment="Center">
              <TextBlock Text="{Binding CurrentStatusText}"/>
            </StatusBarItem>
          </StatusBar>

Chapter 16 Solutions

Exercise 1

To find the answer to this question, you should have a look at the PlayGame() method in the Game.cs file. Have a look through the method and list the variables it references while within the main do…while loop. This information would need to be sent back and forth between the client and server for the game to work via a web site:

  • How many people are playing and what are their names?
  • Who is the current player?
  • The player's hand of cards.
  • The current card in play.
  • The player's action, for example taking, drawing or discarding.
  • A list of discarded cards.
  • The status of the game, such as whether somebody won.

Exercise 2

You can store the information in a database and then retrieve the required data with each call, and you can pass the required information back and forth between the client and server using the ASP.NET Session Object or VIEWSTATE.

For information about the ASP.NET Session Object, read this article: https://msdn.microsoft.com/en-us/library/ms178581.aspx

For information about VIEWSTATE, read this article: https://msdn.microsoft.com/en-us/library/ms972976.aspx

Chapter 17 Solutions

Exercise 1

…
using System.Net;
using System.IO;
using Newtonsoft.Json;
using static System.Console;
namespace handofcards
{
 class Program
 {
  static void Main(string[] args)
  {
   List<string> cards = new List<string>();
   var playerName = "Benjamin";
   string GetURL =
    "http://handofcards.azurewebsites.net/api/HandOfCards/" +
     playerName;
   WebClient client = new WebClient();
   Stream dataStream = client.OpenRead(GetURL);
   StreamReader reader = new StreamReader(dataStream);
   var results =
    JsonConvert.DeserializeObject<dynamic>(reader.ReadLine());
   reader.Close();
   foreach (var item in results)
   {
    WriteLine((string)item.imageLink);
   }
   ReadLine();
  }
 }
}

Exercise 2

The maximum size of a Web App VM is 4 CPU/Cores (˜2.6Ghz) with 7GB of RAM.

The maximum number of VMs that you can have in Standard mode is 10. The maximum number of VMs you can have in Premium mode is 50. That translates into a maximum 200 x 2.6Ghz cores with 350GB of memory loaded across 50 virtual machines.

Note that this is for Web Apps. You can utilize Azure VMs or Azure Cloud Services to get even more cores and memory.

Chapter 18 Solutions

Exercise 1

System.IO

Exercise 2

You use a FileStream object to write to a file when you need random access to files, or when you are not dealing with string data.

Exercise 3

  • Peek(): Gets the value of the next character in the file but does not advance the file position
  • Read(): Gets the value of the next character in the file and advances the file position
  • Read(char[] buffer, int index, int count): Reads count characters into buffer, starting at buffer[index]
  • ReadLine(): Gets a line of text
  • ReadToEnd(): Gets all text in a file

Exercise 4

DeflateStream

Exercise 5

  • Changed: Occurs when a file is modified
  • Created: Occurs when a file in created
  • Deleted: Occurs when a file is deleted
  • Renamed: Occurs when a file is renamed

Exercise 6

Add a button that toggles the value of the FileSystemWatcher.EnableRaisingEvents property.

Chapter 19 Solutions

Exercise 1

  1. Double-click the Create Node button to go to the event handler doing the work.
  2. Below the creation of the XmlComment, insert the following three lines:
                XmlAttribute newPages = document.CreateAttribute("pages");
                newPages.Value = "1000";
            newBook.Attributes.Append(newPages);

Exercise 2

  1. //elements — Returns all nodes in the document.
  2. element — Returns every element node in the document but leaves the element root node out.
  3. element[@Type='Noble Gas'] — Returns every element that includes an attribute with the name Type, which has a value of Noble Gas.
  4. //mass — Returns all nodes with the name mass.
  5. //mass/.. — The .. causes the XPath to move one up from the selected node, which means that this query selects all the nodes that include a mass node.
  6. element/specification[mass='20.1797'] — Selects the specification element that contains a mass node with the value 20.1797.
  7. element/name[text()='Neon'] — To select the node whose contents you are testing, you can use the text() function. This selects the name node with the text Neon.

Exercise 3

Recall that XML can be valid, well-formed, or invalid. Whenever you select part of an XML document, you are left with a fragment of the whole. This means that there is a good chance that the XML you've selected is in fact invalid XML on its own. Most XML viewers will refuse to display XML that isn't well-formed, so it is not possible to display the results of many queries directly in a standard XML viewer.

Exercise 4

Add a new button JSON>XML to MainWindow.xaml and then add the following code to MainWindow.xaml.cs:

        private void buttonConvertXMLtoJSON_Click(object sender, RoutedEventArgs e)
        {
            // Load the XML document.
            XmlDocument document = new XmlDocument();
            document.Load(@"C:BegVCSharpChapter19XML and SchemaBooks.xml");
            string json = Newtonsoft.Json.JsonConvert.SerializeXmlNode(document);
            textBlockResults.Text = json;
            System.IO.File.AppendAllText
                    (@"C:BegVCSharpChapter19XML and SchemaBooks.json", json);
        }
        private void buttonConvertJSONtoXML_Click(object sender, RoutedEventArgs e)
        {
            // Load the json document.
            string json = System.IO.File.ReadAllText              (@"C:BegVCSharpChapter19XML and SchemaBooks.json");
            XmlDocument document =
                Newtonsoft.Json.JsonConvert.DeserializeXmlNode(json);
            textBlockResults.Text =
                FormatText(document.DocumentElement as XmlNode, "", "");
        } 

Chapter 20 Solutions

Exercise 1

static void Main(string[] args)
{
          string[] names = { "Alonso", "Zheng", "Smith", "Jones", "Smythe",
"Small", "Ruiz", "Hsieh", "Jorgenson", "Ilyich", "Singh", "Samba", "Fatimah" };
            var queryResults =
                from n in names
                where n.StartsWith("S")
                orderby n descending
                select n;
            Console.WriteLine("Names beginning with S:");
            foreach (var item in queryResults) {
                Console.WriteLine(item);
            }
           Console.Write("Program finished, press Enter/Return to continue:");
            Console.ReadLine();
}

Exercise 2

Sets smaller than 5,000,000 have no numbers < 1000:

static void Main(string[] args)
{
   int[] arraySizes = {     100,    1000,    10000,  100000,
                        1000000, 5000000, 10000000, 50000000 };
   foreach (int i in arraySizes) {
       int[] numbers = generateLotsOfNumbers(i);
       var queryResults = from n in numbers
                          where n < 1000
                          select n;
       Console.WriteLine("number array size = {0}: Count(n < 1000) = {1}",
                numbers.Length, queryResults.Count()
       );
   }
   Console.Write("Program finished, press Enter/Return to continue:");
   Console.ReadLine();
}

Exercise 3

Does not affect performance noticeably for n < 1000:

 static void Main(string[] args)
 {
     int[] numbers = generateLotsOfNumbers(12345678);
     var queryResults =
         from n in numbers
         where n < 1000
         orderby n
         select n
        ;
     Console.WriteLine("Numbers less than 1000:");
     foreach (var item in queryResults)
     {
         Console.WriteLine(item);
     }
     Console.Write("Program finished, press Enter/Return to continue:");
     Console.ReadLine();
 }

Exercise 4

Very large subsets such as n > 1000 instead of n < 1000 are very slow:

 static void Main(string[] args)
 {
     int[] numbers = generateLotsOfNumbers(12345678);
     var queryResults =
         from n in numbers
         where n > 1000
         select n
        ;
     Console.WriteLine("Numbers less than 1000:");
     foreach (var item in queryResults)
     {
         Console.WriteLine(item);
     }
     Console.Write("Program finished, press Enter/Return to continue:");
     Console.ReadLine();
 }

Exercise 5

All the names are output because there is no query.

static void Main(string[] args)
{
          string[] names = { "Alonso", "Zheng", "Smith", "Jones", "Smythe",
"Small", "Ruiz", "Hsieh", "Jorgenson", "Ilyich", "Singh", "Samba", "Fatimah" };
            var queryResults = names;
            foreach (var item in queryResults) {
                Console.WriteLine(item);
            }
            Console.Write("Program finished, press Enter/Return to continue:");
            Console.ReadLine();
}

Exercise 6

        static void Main(string[] args)
        {
            string[] names = { "Alonso", "Zheng", "Smith", "Jones", "Smythe",
"Small", "Ruiz", "Hsieh", "Jorgenson", "Ilyich", "Singh", "Samba", "Fatimah" };
       // only Min() and Max() are available (if no lambda is used)
       // for a result set like this consisting only of strings
       Console.WriteLine("Min(names) = " + names.Min());
       Console.WriteLine("Max(names) = " + names.Max());
          var queryResults =
                from n in names
                where n.StartsWith("S")
                select n;
        Console.WriteLine("Query result: names starting with S");
         foreach (var item in queryResults)
         {
                Console.WriteLine(item);
         }
        Console.WriteLine("Min(queryResults) = " + queryResults.Min());
        Console.WriteLine("Max(queryResults) = " + queryResults.Max());
         Console.Write("Program finished, press Enter/Return to continue:");
         Console.ReadLine();
        }

Chapter 21 Solutions

Exercise 1

Comment out the explicit creation of the two books and replace with code to prompt for a new title and author such as shown in this code:

//Book book = new Book { Title = "Beginning Visual C# 2015",
//                            Author = "Perkins, Reid, and Hammer" };
//db.Books.Add(book);
//book = new Book { Title = "Beginning XML", Author = "Fawcett, Quin, and Ayers"};
                string title;
                string author;
                Book book;
                do
                {
                    Console.Write("Title: "); title = Console.ReadLine();
                    Console.Write("Author: "); author = Console.ReadLine();
                    if (!string.IsNullOrEmpty(author))
                    {
                        book = new Book { Title = title, Author = author };
                        db.Books.Add(book);
                        db.SaveChanges();
                    }
                } while (!string.IsNullOrEmpty(author)); 

Exercise 2

Add a test LINQ query to see if a book with same title and author already exists before adding to database. Use code like this:

                Book book = new Book { Title = "Beginning Visual C# 2015",
                                       Author = "Perkins, Reid, and Hammer" };
                var testQuery = from b in db.Books
                            where b.Title == book.Title && b.Author == book.Author
                            select b;
                if (testQuery.Count() < 1)
                {
                   db.Books.Add(book);
                   db.SaveChanges();
                }

Exercise 3

Modify the generated classes Stock.cs, Store.cs, and BookContext.cs to use the Inventory and Item names, then change the references to these in Program.cs:

    public partial class Stock
    {
        …
        public virtual Store Store { get; set; }
    }
    public partial class Store
    {
        …
        public Store()
        {
            Inventory = new HashSet<Stock>();
        }
        …
        public virtual ICollection<Stock> Inventory { get; set; }
    }
    public partial class BookContext : DbContext
    {
…
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Book>()
                .HasMany(e => e.Inventory)
                .WithOptional(e => e.Item)
                .HasForeignKey(e => e.Item_Code);
            modelBuilder.Entity<Store>()
                .HasMany(e => e.Inventory)
                .WithOptional(e => e.Store)
                .HasForeignKey(e => e.Store_StoreId);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new BookContext())
            {
                var query = from store in db.Stores
                            orderby store.Name
                            select store;
                foreach (var s in query)
                {
                    XElement storeElement = new XElement("store",
                        new XAttribute("name", s.Name),
                        new XAttribute("address", s.Address),
                        from stock in s.Inventory
                        select new XElement("stock",
                              new XAttribute("StockID", stock.StockId),
                              new XAttribute("onHand",
                               stock.OnHand),
                              new XAttribute("onOrder",
                               stock.OnOrder),
                       new XElement("book",
                       new XAttribute("title",
                           stock.Item.Title),
                       new XAttribute("author",
                           stock.Item.Author)
                       )// end book
                     ) // end stock
                   ); // end store
                   Console.WriteLine(storeElement);
                }

Exercise 4

Use the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace BegVCSharp_21_Exercise4_GhostStories
{
    public class Story
    {
        [Key]
        public int StoryID { get; set; }
        public string Title { get; set; }
        public Author Author { get; set; }
        public string Rating { get; set; }
    }
    public class Author
    {
        [Key]
        public int AuthorId { get; set; }
        public string Name { get; set; }
        public string Nationality { get; set; }
    }
    public class StoryContext : DbContext
    {
        public DbSet<Author> Authors { get; set; }
        public DbSet<Story> Stories { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new StoryContext())
            {
                Author author1 = new Author
                {
                    Name = "Henry James",
                    Nationality = "American"
                };
                Story story1 = new Story
                {
                    Title = "The Turn of the Screw",
                    Author = author1,
                    Rating = "a bit dull"
                };
                db.Stories.Add(story1);
                db.SaveChanges();
                var query = from story in db.Stories
                            orderby story.Title
                            select story;
                Console.WriteLine("Ghost Stories:");
                Console.WriteLine();
                foreach (var story in query)
                {
                    Console.WriteLine(story.Title);
                    Console.WriteLine();
                }
                Console.WriteLine("Press a key to exit…");
                Console.ReadKey();
            }
        }
    }

Chapter 22 Solutions

Exercise 1

All of the above.

Exercise 2

You would implement a data contract, with the DataContractAttribute and DataMemberAttribute attributes.

Exercise 3

Use the .svc extension.

Exercise 4

That is one way of doing things, but it is usually easier to put all your WCF configuration in a separate configuration file, either web.config or app.config.

Exercise 5

[ServiceContract]
public interface IMusicPlayer
{
   [OperationContract(IsOneWay=true)]
   void Play();
   [OperationContract(IsOneWay=true)]
   void Stop();
   [OperationContract]
   TrackInformation GetCurrentTrackInformation();
}

You would also want a data contract to encapsulate track information; TrackInformation in the preceding code.

Chapter 23 Solutions

Exercise 1

  1. Modify the XAML of the page BlankPage1 like this:
    <Page
        x:Class="BasicNavigation.BlankPage1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:BasicNavigation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" Loaded="Page_Loaded">
      <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <CommandBar>
          <AppBarToggleButton x:Name="toggleButtonBold" Icon="Bold" Label="Bold" Click="AppBarToggleButtonBold_Click"/>
          <AppBarSeparator/>
          <AppBarButton Icon="Back" Label="Back" Click="buttonGoBack_Click"/>
          <AppBarButton Icon="Forward" Label="Forward" Click="AppBarButtonForward_Click"/>
          <CommandBar.SecondaryCommands>
            <AppBarButton Icon="Camera" Label="Take picture"/>
            <AppBarButton Icon="Help" Label="Help"/>
          </CommandBar.SecondaryCommands>
        </CommandBar>
        <TextBlock x:Name="textBlockCaption" Text="Page 1" HorizontalAlignment="Center" Margin="10,50,10,10" VerticalAlignment="Top"/>
        <StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Bottom">
          <Button Content="Page 2" Click="buttonGoto2_Click"/>
          <Button Content="Page 3" Click="buttonGoto3_Click"/>
          <Button Content="Back" Click="buttonGoBack_Click"/>
        </StackPanel>
        <WebView x:Name="webViewControl" HorizontalAlignment="Stretch" Margin="0,75,0,40" VerticalAlignment="Stretch"/>
      </Grid>
    </Page>
  2. Go to the code-behind and add these lines:
    webViewControl.Navigate(new Uri("http://www.wrox.com"));
    Application.Current.Resuming += (sender, o) => webViewControl.Navigate(new Uri("http://www.amazon.com/Beginning-Visual-C-2015-Programming/dp/1119096685/ref=sr_1_1?ie=UTF8&qid=1444947234&sr=8-1&keywords=beginning+visual+c%23+2015"));

Exercise 2

You specify which capabilities the app has in the Package.appxmanifest file on the Capabilities tab. In order to avoid getting an UnauthorizedAccessException when you access the microphone, you must ensure that the Microphone capability is checked.

Exercise 3

  1. Create a new Universal app project.
  2. Drag a Pivot control onto the design view.
  3. Change the first PivotItem like this:
          <PivotItem Header="Wrox Homepage">
            <Grid>
              <WebView Name="WebViewControl"/>
              </Grid>
          </PivotItem>
  4. Change the second PivotItem like this:
          <PivotItem Header="Hello Pivot!">
            <Grid>
              <TextBlock Text="Hello Pivot!" HorizontalAlignment="Center" VerticalAlignment="Center"/>
              </Grid>
          </PivotItem>
  5. Add a third PivotItem like this:
          <PivotItem Header="Wrox Logo">
            <Grid>
              <Image Source="http://media.wiley.com/assets/253/59/wrox_logo.gif"/>
            </Grid>
          </PivotItem>
  6. Finally, navigate the web view control to a page you choose by calling Navigate in the constructor of the page:
    WebViewControl.Navigate(new Uri("http://www.wrox.com"));
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset