【问题标题】:Displaying objects using foreach loop使用 foreach 循环显示对象
【发布时间】:2011-12-20 21:51:51
【问题描述】:

我在从 ArrayList 检索数据并将它们显示到文本框中时遇到问题。我收到一个错误:Unable to cast object of type 'Lab_9.Book' to type 'Lab_9.Magazine'。我尝试使用 2 个 foreach 循环,但这似乎是不可能的。我怎样才能避免这个问题?

问题出现在这里:

    // 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;
    }

问候。

【问题讨论】:

    标签: c# object foreach arraylist


    【解决方案1】:

    这将允许您只有一个循环,但它并不漂亮

    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: ");
       }
    }
    

    你真的想定义一个接口来封装所有发布的公共属性和方法,而不是继承。

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

    接下来让你的类实现接口

    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
    }
    

    此时您已经获得了更多的灵活性,但出于您的目的,您可以保持其余代码不变,并且只使用一个更简洁、更符合 jwJung 解决方案的循环

    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);
    }
    

    【讨论】:

    • 这基本上是我最初建议的解决方案。 OP不想重新格式化他的班级结构(因此编辑我的答案)。我真的认为要走这条路,而不是依赖糟糕的程序结构并解决它。 +1
    • 我喜欢这个。我没有太多使用接口。很有教育意义 :) 但至于现在我想我不能把它用作家庭作业,但会在我的最终项目中有用。
    • 使用接口提供了很大的灵活性。如果您有兴趣深入研究该主题,我建议您了解依赖注入(DI),这个关于 SO 的问题是一个很好的起点:stackoverflow.com/questions/130794/what-is-dependency-injection
    • @Jason:谢谢,我相信我会看看这个。
    • 我会在这里使用抽象基类而不是接口。 msdn.microsoft.com/en-us/library/scsyfw1d(v=vs.71).aspx
    【解决方案2】:

    您的 pub 对象 (ArrayList) 的项目是 Book 和 Magazine 类型的 Object 类型实例。因此,您需要使用 .OfType() 进行过滤以显示每种类型。此方法将仅返回 ArrayList 中目标类型 (T) 的实例。

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

    要结合这两个 foreach,我建议你重写 ToString() 或创建一个具有字符串属性的基类。要使用基类,请将所有值(例如 title + "," + bookNumber...) 设置为字符串属性。我将向您展示重写 ToString()。

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

    然后你可以将两个循环结合起来。

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

    【讨论】:

    • 这看起来很棒,而且不会引起问题。另一个问题。有没有办法将两个对象集成到一个 foreach 循环中?这么说我的意思是,如果我可以按照写入方式从数组中读取所有元素。
    • 似乎是一个很好的解决方案。我将在我的最终项目中需要它。非常感谢。但问题是我使用了类图,教授要求我们不要改变任何东西。
    • 如果是这样,请使用覆盖 ToSrig() 而不是创建基类。制作基类应该修改类设计,但是对于重写的ToString(),我们并没有说设计(图表)被改变了。在这里,对于我理解为类设计的图表。
    • @HelpNeeder:不知道你想说什么。在我看来,您将书籍和杂志都视为出版物,它们都有作者和书号。这表明了一定程度的共性。它们可能都应该从名为 Publication 的东西中派生出来,而不是将它们存储在对象数组中,而应该将它们存储在 Publications 的数组(或列表)中。理想情况下。不确定这是否违反要求。 编辑: Jason 所说的。虽然我会使用抽象基类,而不是接口。
    • @Mark:对不起,我措辞不正确。正如您提到的,发布类是我的基类。 Book 和 Magazine 类是派生类。你是绝对正确的。
    【解决方案3】:

    编辑:我重新阅读了您的问题和您的评论(对此感到抱歉),我认为您正在寻找这个:

    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>         
        }
    } 
    

    【讨论】:

    • 我使用了类图,它指出了这个文件的所有元素。这个文件不需要继承任何东西。有没有办法修改这个循环,使它适用于我的场景?
    • 你已经解决了我的“做一个循环”的问题。谢谢你。无需修改结构即可工作,并且一个循环可以完美运行。
    猜你喜欢
    • 2013-05-20
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 2018-04-02
    • 1970-01-01
    • 2015-03-27
    相关资源
    最近更新 更多