HelpNeeder
HelpNeeder

Reputation: 6490

Displaying objects using foreach loop

I am having a problem while retrieving data from an ArrayList and displaying them into textboxs. I am getting an error: Unable to cast object of type 'Lab_9.Book' to type 'Lab_9.Magazine'. I tried use 2 foreach loops but that seems to be out of the question. How could I avoid this problem?

Problem occurs here:

    // Displaying all Book objects from pubs ArrayList.
    foreach (Book list in pubs)
    {
        bookNumber++; // Count books from the begining.

        // Displaying and formating the output 
        // in txtBookList textbox.
        bookList.txtBookList.Text +=
            "=== Book " + bookNumber + " ===" + Environment.NewLine +
            list.Title + Environment.NewLine +
            list.getAuthorName() + Environment.NewLine +
            list.PublisherName + Environment.NewLine +
            "$" + list.Price + Environment.NewLine
            + Environment.NewLine;
    }

Regards.

Upvotes: 2

Views: 2685

Answers (3)

Jason
Jason

Reputation: 15931

this will allow you to only have a single loop, but it's not pretty

foreach (object publication in pubs)
{
   var book = publication as Book;
   var magazine = publication as Magazine;
   if (book != null) {
     //it's a book, do your thing
   } else if (magazine != null) {
     //it's a magazine, do your thing
   } else {
    throw new InvalidOperationException(publication.GetType().Name + " is not a book or magazine: ");
   }
}

Instead of inheritance, you really want to define an interface that encapsulates the common properties and methods of all publications.

public interface IPublication
{
  string Title {get;set;}
  float Price {get;set;}
  // etc.
}

Next have your classes implement the interface

public class Book : IPublication
{
  public string Title {get;set;}
  public float Price {get;set;}
  //the rest of the book implementation
}

public class Magazine: IPublication

{
  public string Title {get;set;}
  public float Price {get;set;}
  //the rest of the magazine implementation
}

you've got a lot more flexibility at this point, but for your purposes you can keep the rest of the code as is and use just a single loop that is much cleaner, and more inline with jwJung's solution

foreach (var publication in pubs.OfType<IPublication>())
{
  // publication is either a book or magazine, we don't care we are just interested in the common properties
  Console.WriteLine("{0} costs {1}",  publication.Title, publication.Price);
}

Upvotes: 3

Jin-Wook Chung
Jin-Wook Chung

Reputation: 4344

The items of your pub object(ArrayList) are Object type instances for Book and Magazine types. So you need to filter using .OfType() to display each type. This method will returns only the instances of the target type(T) in ArrayList.

foreach (Book list in pubs.OfType<Book>())
{
}

foreach (Magazine list in pubs.OfType<Magazine>())
{
}

To combine the two foreach, I will suggest you to override ToString() or to make a base class having the string property. For using the base class, set all of values(eg title + "," + bookNumber...) to the string property. I will show you overriding ToString().

internal class Book
{
    public override string ToString()
    {
        return "=== Book " + bookNumber + " ===" + Environment.NewLine +....;
    }
}

internal class Magazine
{
    public override string ToString()
    {
        return "=== Publication: " + bookNumber + ....;
    }
}

And then you can combine the two loop.

foreach (string item in pub)
{
    Console.WriteLine(item.ToString());
}

Upvotes: 2

Paul Zaczkowski
Paul Zaczkowski

Reputation: 2888

Edit: I've re-read your question, and your comment (sorry about that), I think what you're looking for this:

foreach (var list in pubs)
{
    if(list is Book)
    {
        Book tmp = (Book)list;
        // Print tmp.<values> 
    }

    if(list is Magazine)
    { 
        Magazine tmp = (Magazine)list;
        // Print tmp.<values>         
    }
} 

Upvotes: 1

Related Questions