开发者

Using yield return to return objects that inherit form a base type

开发者 https://www.devze.com 2022-12-09 15:02 出处:网络
I have a bas class called Media with two classes that inherit from it, Photo and Video. I am trying to create a collection for the media base class to hold those photo and video objects. So I have cre

I have a bas class called Media with two classes that inherit from it, Photo and Video. I am trying to create a collection for the media base class to hold those photo and video objects. So I have created a MediaList class as follows:

public class MediaList: ICollection<Media>
{
    private readonly XElement _mediaElement;

    public MediaList(XElement mediaElement)
    {
        _mediaElement = mediaElement;            
    }

    public IEnumerator<Media> GetEnumerator()
    {
        foreach (XElement element in _mediaElement.Elements())
        {
            Media media;
            switch (element.Name.LocalName)
            {
                case "video":
                    media = new Video(element);
                    br开发者_StackOverflow社区eak;
                case "photo":
                    media = new Photo(element);
                    break;
                default:
                    media = null;
                    break;
            }
            yield return media;
        }
    }

    //Rest of ICollection Implementation
}

When I iterate the list I get the following exception:

The value "Tool.Photo" is not of type "Tool.Video" and cannot be used in this generic collection.

If I am returning a Media object, why is it throwing the exception? Is there a better way to get around this?


Try this...

class Program
{
    static void Main(string[] args)
    {
        var array = new string[] { "video", "photo", "hurf", "photo" };
        var ml = new MediaList(array);
        foreach(var element in ml)
            Console.WriteLine(element.GetType().Name);
        Console.Read();
    }
}

public class Media { }
public class Video : Media { }
public class Photo : Media { }

public class MediaList
{
    private string[] elements;
    public MediaList(string[] elements) { this.elements = elements; }
    public IEnumerator<Media> GetEnumerator()
    {
        foreach (string s in elements)
            switch (s)
            {
                case "video":
                    yield return new Video();
                    break;
                case "photo":
                    yield return new Photo();
                    break;
            }
    }
}

You can slap this into a console app to test it.

Notice a couple different things. First, you never yield return a null. That doesn't have anything to do with your issue, but nobody expects an enumerable to return a null and will cause you problems later on. Second, I'm not casting what I return. All casting is implicit or handled by the compiler, so there isn't any need for me to do this. Third, this compiles and works, as does your original code. Your issue is happening somewhere else, as you will find if you drop this code into a console app and test it.


Your mistake is elsewhere. What you have written is fine, as long as both Video and Photo inherit from Media. Maybe you are trying to cast it incorrectly somewhere else.


Shouldn't the method with the yield return be typed to return IEnumerable<Media>, not IEnumerator<Media> ?

But more basically, this pattern leaves me with a bad smell. Every time you use foreach on this "collection", you will be creating (instantiating) new instances of each Photo or Video object in the _mediaElement.Elements() list. Is this really what you want?


Looks like you're trying to add Media instances into a Video collection, and it blows up because one or more of them is a Photo -- you're trying to add a photo to a video collection. The error is in your client code, not in the generated enumerator.

0

精彩评论

暂无评论...
验证码 换一张
取 消